diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+0.7.0.0
+---
+* Updated number representation to preserve fractional part
+  and added new `Config.Number` module with operations on
+  this new type.
+
 0.6.3.1
 ---
 * Build on GHC 8.4.1
diff --git a/config-value.cabal b/config-value.cabal
--- a/config-value.cabal
+++ b/config-value.cabal
@@ -1,47 +1,64 @@
+cabal-version:       2.2
 name:                config-value
-version:             0.6.3.1
+version:             0.7.0.0
 synopsis:            Simple, layout-based value language similar to YAML or JSON
 license:             MIT
 license-file:        LICENSE
 author:              Eric Mertens
 maintainer:          emertens@gmail.com
-copyright:           2015-2016 Eric Mertens
+copyright:           2015-2016,2019 Eric Mertens
 category:            Language
 build-type:          Simple
-cabal-version:       >=1.10
 homepage:            https://github.com/glguy/config-value
 bug-reports:         https://github.com/glguy/config-value/issues
 description:         This package implements a language similar to YAML or JSON but
                      with fewer special cases and fewer dependencies. It emphasizes
                      layout structure for sections and lists, and requires quotes
                      around strings.
-tested-with:         GHC==7.10.3, GHC==8.0.2, GHC==8.2.1
+tested-with:         GHC==8.0.2, GHC==8.2.2, GHC==8.4.4, GHC==8.6.5, GHC==8.8.1
 
-extra-source-files:    README.md
-                       CHANGELOG.md
-                       config-value.vim
+extra-source-files:
+  README.md
+  CHANGELOG.md
+  config-value.vim
 
 library
 
-  exposed-modules:     Config,
-                       Config.Lens
+  exposed-modules:
+    Config
+    Config.Lens
+    Config.Number
 
-  other-modules:       Config.Lexer,
-                       Config.LexerUtils,
-                       Config.Parser,
-                       Config.Tokens,
-                       Config.Pretty,
-                       Config.Value
+  other-modules:
+    Config.Lexer
+    Config.LexerUtils
+    Config.Parser
+    Config.NumberParser
+    Config.Tokens
+    Config.Pretty
+    Config.Value
 
-  build-depends:       base       >= 4.8     && < 4.12,
-                       array      >= 0.4     && < 0.6,
-                       pretty     >= 1.1.1.0 && < 1.2,
-                       text       >= 1.2.0.4 && < 1.3
+  build-depends:
+    base       >= 4.8     && < 4.14,
+    array      >= 0.4     && < 0.6,
+    pretty     >= 1.1.1.0 && < 1.2,
+    text       >= 1.2.0.4 && < 1.3
 
+  build-tool-depends:
+    alex:alex   ^>= 3.2.4,
+    happy:happy ^>= 1.19.12,
+
   hs-source-dirs:      src
-  build-tools:         alex, happy
   default-language:    Haskell2010
 
 source-repository head
   type: git
   location: git://github.com/glguy/config-value.git
+
+test-suite unit-tests
+  type:                 exitcode-stdio-1.0
+  main-is:              Main.hs
+  hs-source-dirs:       test
+  build-depends:        base, config-value, text
+  default-language:     Haskell2010
+  ghc-options:          -Wall
diff --git a/src/Config.hs b/src/Config.hs
--- a/src/Config.hs
+++ b/src/Config.hs
@@ -250,10 +250,18 @@
   , Atom(..)
   , valueAnn
 
+  -- * Numbers
+  , Number
+  , numberToInteger
+  , numberToRational
+  , integerToNumber
+  , rationalToNumber
+
   -- * Errors
   , ParseError(..)
   ) where
 
+import           Config.Number (Number, numberToInteger, numberToRational, integerToNumber, rationalToNumber)
 import           Config.Value  (Atom(..), Value(..), Section(..), valueAnn)
 import           Config.Parser (parseValue)
 import           Config.Pretty (pretty)
@@ -296,16 +304,12 @@
 explainToken token =
   case token of
     T.Error e     -> explainError e
-    T.Floating{}  -> "parse error: unexpected floating-point literal"
     T.Atom atom   -> "parse error: unexpected atom: `" ++ Text.unpack atom ++ "`"
     T.String str  -> "parse error: unexpected string: " ++ show (Text.unpack str)
     T.Bullet      -> "parse error: unexpected bullet '*'"
     T.Comma       -> "parse error: unexpected comma ','"
     T.Section s   -> "parse error: unexpected section: `" ++ Text.unpack s ++ "`"
-    T.Number 2  n -> "parse error: unexpected number: " ++ showIntAtBase' "0b"  2 intToDigit n ""
-    T.Number 8  n -> "parse error: unexpected number: " ++ showIntAtBase' "0o"  8 intToDigit n ""
-    T.Number 16 n -> "parse error: unexpected number: " ++ showIntAtBase' "0x" 16 intToDigit n ""
-    T.Number _  n -> "parse error: unexpected number: " ++ showIntAtBase' ""   10 intToDigit n ""
+    T.Number{}    -> "parse error: unexpected number"
     T.OpenList    -> "parse error: unexpected start of list '['"
     T.CloseList   -> "parse error: unexpected end of list ']'"
     T.OpenMap     -> "parse error: unexpected start of section '{'"
diff --git a/src/Config/Lens.hs b/src/Config/Lens.hs
--- a/src/Config/Lens.hs
+++ b/src/Config/Lens.hs
@@ -10,8 +10,8 @@
 module Config.Lens
   ( key
   , text
-  , number
   , atom
+  , number
   , list
   , values
   , sections
@@ -19,6 +19,7 @@
   , valuePlate
   ) where
 
+import Config.Number
 import Config.Value
 import Data.Text
 
@@ -56,11 +57,10 @@
 atom f (Atom a t) = Atom a <$> f t
 atom _ v          = pure v
 
--- | Traversal for the 'Integer' contained inside the given
--- 'Value' when it is a 'Number'.
-number :: Applicative f => (Integer -> f Integer) -> Value a -> f (Value a)
-number f (Number a b n) = Number a b <$> f n
-number _ v              = pure v
+-- | Traversal for the 'Number' contained inside the given 'Value'.
+number :: Applicative f => (Number -> f Number) -> Value a -> f (Value a)
+number f (Number a n) = Number a <$> f n
+number _ v            = pure v
 
 -- | Traversal for the ['Value'] contained inside the given
 -- 'Value' when it is a 'List'.
@@ -93,9 +93,8 @@
 ann :: Functor f => (a -> f a) -> Value a -> f (Value a)
 ann f v =
   case v of
-    Sections a x   -> (\a' -> Sections a' x  ) <$> f a
-    Number   a x y -> (\a' -> Number   a' x y) <$> f a
-    Floating a x y -> (\a' -> Floating a' x y) <$> f a
-    Text     a x   -> (\a' -> Text     a' x  ) <$> f a
-    Atom     a x   -> (\a' -> Atom     a' x  ) <$> f a
-    List     a x   -> (\a' -> List     a' x  ) <$> f a
+    Sections a x -> (\a' -> Sections a' x) <$> f a
+    Number   a x -> (\a' -> Number   a' x) <$> f a
+    Text     a x -> (\a' -> Text     a' x) <$> f a
+    Atom     a x -> (\a' -> Atom     a' x) <$> f a
+    List     a x -> (\a' -> List     a' x) <$> f a
diff --git a/src/Config/Lexer.x b/src/Config/Lexer.x
--- a/src/Config/Lexer.x
+++ b/src/Config/Lexer.x
@@ -52,6 +52,7 @@
 @atom           = $alpha [$alpha $digit $unidigit \. _ \-]*
 
 @exponent       = [Ee] [\-\+]? @decimal
+@hexexponent    = [Pp] [\-\+]? @decimal
 
 config :-
 
@@ -65,11 +66,11 @@
 ","                     { token_ Comma                  }
 "]"                     { token_ CloseList              }
 "*"                     { token_ Bullet                 }
-"-"? 0 [Xx] @hexadecimal{ token (number 2 16)           }
-"-"? 0 [Oo] @octal      { token (number 2  8)           }
-"-"? 0 [Bb] @binary     { token (number 2  2)           }
-"-"? @decimal           { token (number 0 10)           }
-"-"? @decimal ("." @decimal)? @exponent? { token floating }
+
+"-"? 0 [Xx] @hexadecimal ("." @hexadecimal?)? @hexexponent? { token number }
+"-"? 0 [Oo] @octal       ("." @octal      ?)?               { token number }
+"-"? 0 [Bb] @binary      ("." @binary     ?)?               { token number }
+"-"?        @decimal     ("." @decimal    ?)? @exponent?    { token number }
 @atom                   { token Atom                    }
 @atom $white_no_nl* :   { token section                 }
 \"                      { startString                   }
diff --git a/src/Config/LexerUtils.hs b/src/Config/LexerUtils.hs
--- a/src/Config/LexerUtils.hs
+++ b/src/Config/LexerUtils.hs
@@ -20,7 +20,6 @@
   , token_
   , section
   , number
-  , floating
 
   -- * Final actions
   , untermString
@@ -28,14 +27,17 @@
   , errorAction
   ) where
 
-import Data.Char            (GeneralCategory(..), generalCategory, digitToInt,
-                             isAscii, isSpace, ord, isDigit)
-import Data.Text            (Text)
-import Data.Word            (Word8)
-import Numeric              (readInt)
-import qualified Data.Text      as Text
+import           Control.Applicative
+import           Data.Char (GeneralCategory(..), generalCategory, digitToInt,
+                            isAscii, isSpace, ord, isDigit, isHexDigit)
+import           Data.Text (Text)
+import           Data.Word (Word8)
+import           Numeric   (readInt, readHex)
+import qualified Data.Text as Text
 
-import Config.Tokens
+import           Config.Tokens
+import           Config.Number
+import qualified Config.NumberParser
 
 ------------------------------------------------------------------------
 -- Custom Alex wrapper - these functions are used by generated code
@@ -148,39 +150,9 @@
 -- given base. This function expect the token to be
 -- legal for the given base. This is checked by Alex.
 number ::
-  Int  {- ^ prefix length      -} ->
-  Int  {- ^ base               -} ->
   Text {- ^ sign-prefix-digits -} ->
   Token
-number prefixLen base str =
-  case readInt (fromIntegral base) (const True) digitToInt str2 of
-    [(n,"")] -> Number base (s*n)
-    _        -> error "number: Lexer failure"
-  where
-  str2     = drop prefixLen str1
-  (s,str1) = case Text.unpack str of
-               '-':rest -> (-1, rest)
-               rest     -> ( 1, rest)
-
--- | Construct a 'Floating' token from a lexeme.
-floating ::
-  Text {- ^ sign-integer-[. decimal][e exponent] -} ->
-  Token
-floating str = Floating (s * read (x1++x2)) (x3-fromIntegral (length x2))
-
-  where
-  (s,str1) = case Text.unpack str of
-               '-':rest -> (-1, rest)
-               rest     -> ( 1, rest)
-  (x1,str2) = span isDigit str1
-  (x2,str3) = case str2 of
-                '.':xs -> span isDigit xs
-                _ -> ("", str2)
-  x3        = case str3 of
-                []        -> 0
-                _e:'+':xs -> read xs
-                _e:xs     -> read xs
-
+number = Number . Config.NumberParser.number . Text.unpack . Text.toUpper
 
 -- | Process a section heading token
 section :: Text -> Token
diff --git a/src/Config/Number.hs b/src/Config/Number.hs
new file mode 100644
--- /dev/null
+++ b/src/Config/Number.hs
@@ -0,0 +1,88 @@
+{-# Language DeriveDataTypeable, DeriveGeneric #-}
+{-|
+Module      : Config.Number
+Description : Scientific-notation numbers with explicit radix
+Copyright   : (c) Eric Mertens, 2019
+License     : ISC
+Maintainer  : emertens@gmail.com
+
+This module provides a representation of numbers in scientific
+notation.
+-}
+module Config.Number
+  ( Number(..)
+  , Radix(..)
+  , radixToInt
+  , numberToRational
+  , numberToInteger
+  , integerToNumber
+  , rationalToNumber
+  ) where
+
+import Data.Ratio (numerator, denominator)
+import Data.Data (Data)
+import GHC.Generics (Generic)
+
+-- | Numbers are represented as base, coefficient, and exponent.
+--
+-- The most convenient way to get numbers into and out of this form
+-- is to use one of: 'numberToRational', 'numberToInteger',
+-- 'rationalToNumber', or 'integerToNumber'.
+--
+-- This representation is explicit about the radix and exponent
+-- used to facilitate better pretty-printing. By using explicit
+-- exponents extremely large numbers can be represented compactly.
+-- Consider that it is easy to write `1e100000000` which would use
+-- a significant amount of memory if realized as an 'Integer'. This
+-- representation allows concerned programs to check bounds before
+-- converting to a representation like 'Integer'.
+data Number = MkNumber
+  { numberRadix       :: !Radix
+  , numberCoefficient :: !Rational
+  }
+  deriving (Eq, Ord, Read, Show, Data, Generic)
+
+-- | Radix used for a number. Some radix modes support an
+-- exponent.
+data Radix
+  = Radix2           -- ^ binary, base 2
+  | Radix8           -- ^ octal, base 8
+  | Radix10 !Integer -- ^ decimal, base 10, exponent base 10
+  | Radix16 !Integer -- ^ hexdecimal, base 16, exponent base 2
+  deriving (Eq, Ord, Read, Show, Data, Generic)
+
+-- | Returns the radix as an integer ignoring any exponent.
+radixToInt :: Radix -> Int
+radixToInt r =
+  case r of
+    Radix2 {} ->  2
+    Radix8 {} ->  8
+    Radix10{} -> 10
+    Radix16{} -> 16
+
+-- | Convert a number to a 'Rational'. Warning: This can use a
+-- lot of member in the case of very large exponent parts.
+numberToRational :: Number -> Rational
+numberToRational (MkNumber r c) =
+  case r of
+    Radix2    -> c
+    Radix8    -> c
+    Radix10 e -> c * 10 ^^ e
+    Radix16 e -> c * 2  ^^ e
+
+-- | Convert a number to a 'Integer'. Warning: This can use a
+-- lot of member in the case of very large exponent parts.
+numberToInteger :: Number -> Maybe Integer
+numberToInteger n
+  | denominator r == 1 = Just $! numerator r
+  | otherwise          = Nothing
+  where
+    r = numberToRational n
+
+-- | 'Integer' to a radix 10 'Number' with no exponent
+integerToNumber :: Integer -> Number
+integerToNumber = rationalToNumber . fromInteger
+
+-- | 'Rational' to a radix 10 'Number' with no exponent
+rationalToNumber :: Rational -> Number
+rationalToNumber r = (MkNumber (Radix10 0) r)
diff --git a/src/Config/NumberParser.y b/src/Config/NumberParser.y
new file mode 100644
--- /dev/null
+++ b/src/Config/NumberParser.y
@@ -0,0 +1,125 @@
+{
+{-# LANGUAGE Trustworthy #-}
+
+module Config.NumberParser where
+
+import Data.List (foldl')
+import Config.Number
+
+}
+
+%tokentype
+    { Char}
+%token
+'+' { '+' }
+'-' { '-' }
+'.' { '.' }
+'0' { '0' }
+'1' { '1' }
+'2' { '2' }
+'3' { '3' }
+'4' { '4' }
+'5' { '5' }
+'6' { '6' }
+'7' { '7' }
+'8' { '8' }
+'9' { '9' }
+'A' { 'A' }
+'B' { 'B' }
+'C' { 'C' }
+'D' { 'D' }
+'E' { 'E' }
+'F' { 'F' }
+'O' { 'O' }
+'P' { 'P' }
+'X' { 'X' }
+
+%name number
+
+%%
+
+number ::                               { Number        }
+  : '-' unsigned_number                 { negNum $2     }
+  |     unsigned_number                 {        $1     }
+
+unsigned_number
+  : '0' 'X' hexadecimal fracpart(hexadecimal) exppart('P')
+                                        { mkNum (Radix16 $5) $3 $4 }
+  |         decimal     fracpart(decimal    ) exppart('E')
+                                        { mkNum (Radix10 $3) $1 $2 }
+  | '0' 'O' octal       fracpart(octal      )
+                                        { mkNum Radix8 $3 $4 }
+  | '0' 'B' binary      fracpart(binary     )
+                                        { mkNum Radix2 $3 $4 }
+
+fracpart(p) ::                          { [Int]         }
+  :                                     { []            }
+  | '.'                                 { []            }
+  | '.' p                               { $2            }
+
+exppart(p) ::                           { Integer       }
+  :                                     { 0             }
+  | p expnum                            { $2            }
+
+
+expnum ::                               { Integer       }
+  : '+' decimal                         {   toInt 10 $2 }
+  | '-' decimal                         { - toInt 10 $2 }
+  |     decimal                         {   toInt 10 $1 }
+
+hexadecimal ::                          { [Int]         }
+  :             hexdigit                { [$1]          }
+  | hexadecimal hexdigit                { $2 : $1       }
+
+decimal ::                              { [Int]         }
+  :         decdigit                    { [$1]          }
+  | decimal decdigit                    { $2 : $1       }
+
+octal ::                                { [Int]         }
+  :       octdigit                      { [$1]          }
+  | octal octdigit                      { $2 : $1       }
+
+binary ::                               { [Int]         }
+  :        bindigit                     { [$1]          }
+  | binary bindigit                     { $2 : $1       }
+
+hexdigit :: { Int }
+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}
+  | '8' { 8} | '9' { 9} | 'A' {10} | 'B' {11}
+  | 'C' {12} | 'D' {13} | 'E' {14} | 'F' {15}
+
+decdigit
+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}
+  | '8' { 8} | '9' { 9}
+
+octdigit :: { Int }
+  : '0' { 0} | '1' { 1} | '2' { 2} | '3' { 3}
+  | '4' { 4} | '5' { 5} | '6' { 6} | '7' { 7}
+
+bindigit :: { Int }
+  : '0' { 0} | '1' { 1}
+
+{
+
+mkNum :: Radix -> [Int] -> [Int] -> Number
+mkNum radix coef frac =
+  MkNumber radix (fromInteger (toInt base coef) + toFrac base frac)
+  where
+    base = radixToInt radix
+
+negNum :: Number -> Number
+negNum n = n { numberCoefficient = - numberCoefficient n }
+
+toInt :: Int -> [Int] -> Integer
+toInt base = foldl' (\acc i -> acc*base' + fromIntegral i) 0 . reverse
+  where base' = fromIntegral base
+
+toFrac :: Int -> [Int] -> Rational
+toFrac base = foldl' (\acc i -> (fromIntegral i+acc)/base') 0
+  where base' = fromIntegral base
+
+happyError [] = error "Unexpected EOF"
+happyError (c:_) = error ("Unexpected: "++[c])
+}
diff --git a/src/Config/Parser.y b/src/Config/Parser.y
--- a/src/Config/Parser.y
+++ b/src/Config/Parser.y
@@ -15,7 +15,6 @@
 STRING                          { Located _ T.String{}          }
 ATOM                            { Located _ T.Atom{}            }
 NUMBER                          { Located _ T.Number{}          }
-FLOATING                        { Located _ T.Floating{}        }
 '*'                             { Located $$ T.Bullet            }
 '['                             { Located $$ T.OpenList          }
 ','                             { Located _ T.Comma             }
@@ -43,7 +42,6 @@
 
 simple ::                       { Value Position                }
   : NUMBER                      { number   $1                   }
-  | FLOATING                    { floating $1                   }
   | STRING                      { text     $1                   }
   | ATOM                        { atom     $1                   }
   | '{' inlinesections '}'      { Sections $1 (reverse $2)      }
@@ -88,16 +86,10 @@
 {
 
 -- | Convert number token to number value. This needs a custom
--- function like this because there are two value matched from
+-- function like this because there are multiple values matched from
 -- the constructor.
 number :: Located Token -> Value Position
-number = \(Located a (T.Number base val)) -> Number a base val
-
--- | Convert floating token to floating value. This needs a custom
--- function like this because there are two value matched from
--- the constructor.
-floating :: Located Token -> Value Position
-floating = \(Located a (T.Floating coef expo)) -> Floating a coef expo
+number = \(Located a (T.Number n)) -> Number a n
 
 section :: Located Token -> Value Position -> Section Position
 section = \(Located a (T.Section k)) v -> Section a k v
diff --git a/src/Config/Pretty.hs b/src/Config/Pretty.hs
--- a/src/Config/Pretty.hs
+++ b/src/Config/Pretty.hs
@@ -3,12 +3,14 @@
 
 import           Data.Char (isPrint, isDigit,intToDigit)
 import           Data.List (mapAccumL)
+import           Data.Ratio (numerator, denominator)
 import qualified Data.Text as Text
 import           Text.PrettyPrint
 import           Numeric(showIntAtBase)
 import           Prelude hiding ((<>))
 
-import Config.Value
+import           Config.Value
+import           Config.Number
 
 -- | Pretty-print a 'Value' as shown in the example.
 -- Sections will nest complex values underneath with
@@ -19,26 +21,38 @@
   case value of
     Sections _ [] -> text "{}"
     Sections _ xs -> prettySections xs
-    Number _ b n  -> prettyNum b n
-    Floating _ c e-> prettyFloating c e
+    Number _ n    -> prettyNumber n
     Text _ t      -> prettyText (Text.unpack t)
     Atom _ t      -> text (Text.unpack (atomName t))
     List _ []     -> text "[]"
     List _ xs     -> vcat [ char '*' <+> pretty x | x <- xs ]
 
-
-prettyNum :: Int -> Integer -> Doc
-prettyNum b n
-  | b == 16   = pref <> text "0x" <> num
-  | b ==  8   = pref <> text "0o" <> num
-  | b ==  2   = pref <> text "0b" <> num
-  | otherwise = integer n
+prettyNumber :: Number -> Doc
+prettyNumber (MkNumber r c) =
+  case r of
+    Radix16 e -> pref <> text "0x" <> num <> expPart 'p' e
+    Radix10 e -> pref <>              num <> expPart 'e' e
+    Radix8    -> pref <> text "0o" <> num
+    Radix2    -> pref <> text "0b" <> num
   where
-  pref = if n < 0 then char '-' else empty
-  num  = text (showIntAtBase (fromIntegral b) intToDigit (abs n) "")
+    radix = radixToInt r
+    pref = if c < 0 then char '-' else empty
+    num  = text (showIntAtBase (fromIntegral radix) intToDigit whole "") -- XXX
+           <> fracPart frac
+    (whole,frac) = properFraction (abs c)
+    expPart _ 0 = text ""
+    expPart c i = text (c : show i)
+    fracPart 0 = text ""
+    fracPart i = text ('.' : showFrac radix frac)
 
-prettyFloating :: Integer -> Integer -> Doc
-prettyFloating c e = text (show c ++ "e" ++ show e)
+showFrac :: Int -> Rational -> String
+showFrac radix 0 = ""
+showFrac radix x = show w ++ rest
+  where
+    (w,f) = properFraction (x * fromIntegral radix)
+    rest
+      | denominator f < denominator x = showFrac radix f
+      | otherwise = ""
 
 prettyText :: String -> Doc
 prettyText = doubleQuotes . cat . snd . mapAccumL ppChar True
@@ -79,6 +93,3 @@
 isBig (Sections _ (_:_)) = True
 isBig (List _ (_:_))     = True
 isBig _                  = False
-
-
-
diff --git a/src/Config/Tokens.hs b/src/Config/Tokens.hs
--- a/src/Config/Tokens.hs
+++ b/src/Config/Tokens.hs
@@ -10,6 +10,7 @@
   ) where
 
 import Data.Text (Text)
+import Config.Number
 
 -- | A position in a text file
 data Position = Position
@@ -37,8 +38,7 @@
   | Atom Text
   | Bullet
   | Comma
-  | Number Int Integer
-  | Floating Integer Integer
+  | Number Number
   | OpenList
   | CloseList
   | OpenMap
diff --git a/src/Config/Value.hs b/src/Config/Value.hs
--- a/src/Config/Value.hs
+++ b/src/Config/Value.hs
@@ -1,7 +1,7 @@
 {-# Language DeriveGeneric, DeriveTraversable, DeriveDataTypeable #-}
 
 -- | This module provides the types used in this package for configuration.
--- Visit "ConfigFile.Parser" to parse values of this type in a convenient
+-- Visit "Config.Parser" to parse values of this type in a convenient
 -- layout based notation.
 module Config.Value
   ( Section(..)
@@ -15,11 +15,13 @@
 import Data.String  (IsString(..))
 import GHC.Generics (Generic, Generic1)
 
+import Config.Number (Number)
+
 -- | A single section of a 'Value'
 --
 -- Example:
 --
---    * @my-key: my-value@ is @'Section' ('Atom' "my-key") ('Atom' "my-value")@
+--    * @my-key: my-value@ is @'Section' _ ('Atom' _ "my-key") ('Atom' _ "my-value")@
 data Section a = Section
   { sectionAnn   :: a
   , sectionName  :: Text
@@ -43,31 +45,15 @@
 
 -- | Sum type of the values supported by this language.
 --
--- The first field of the 'Number' constructor is the based used in the concrete
--- syntax of the configuration value.
---
--- The 'Floating' constructor stores the coefficient and power-of-10 exponent used in
--- the concrete syntax. This allows representing numbers that would
--- otherwise overflow a 'Double'.
---
 -- 'Value' is parameterized over an annotation type indented to be used for
--- file position or other application specific information.
---
--- Examples:
---
---    * @0xff@ is @'Number' 16 255@
---
---    * @123@  is @'Number' 10 123@
---
---    * @123e10@ is @'Floating' 123 10@
---    * @123.45@ is @'Floating' 12345 (-2)@
+-- file position or other application specific information. When no
+-- annotations are needed, '()' is a fine choice.
 data Value a
   = Sections a [Section a] -- ^ lists of key-value pairs
-  | Number   a Int Integer -- ^ integer literal base (2, 8, 10, or 16) and integer value
-  | Floating a Integer Integer -- ^ coef exponent: coef * 10 ^ exponent
-  | Text     a Text -- ^ quoted strings
-  | Atom     a Atom -- ^ unquoted strings
-  | List     a [Value a] -- ^ lists
+  | Number   a Number      -- ^ numbers
+  | Text     a Text        -- ^ quoted strings
+  | Atom     a Atom        -- ^ unquoted strings
+  | List     a [Value a]   -- ^ lists
   deriving ( Eq, Read, Show, Typeable, Data
            , Functor, Foldable, Traversable
            , Generic, Generic1
@@ -77,9 +63,8 @@
 valueAnn :: Value a -> a
 valueAnn v =
   case v of
-    Sections a _   -> a
-    Number   a _ _ -> a
-    Floating a _ _ -> a
-    Text     a _   -> a
-    Atom     a _   -> a
-    List     a _   -> a
+    Sections a _ -> a
+    Number   a _ -> a
+    Text     a _ -> a
+    Atom     a _ -> a
+    List     a _ -> a
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,104 @@
+{-# Language OverloadedStrings #-}
+{-|
+Module      : Main
+Description : Unit tests for config-schema
+Copyright   : (c) Eric Mertens, 2017
+License     : ISC
+Maintainer  : emertens@gmail.com
+-}
+module Main (main) where
+
+import           Config
+import           Config.Number
+import           Control.Monad (unless)
+import           Data.Foldable
+import           Data.Text (Text)
+import qualified Data.Text as Text
+
+parseTest ::
+  Value () {- ^ expected value -} ->
+  [Text]   {- ^ input lines    -} ->
+  IO ()
+parseTest expected txts =
+  case parse (Text.unlines txts) of
+    Left e -> fail (show e)
+    Right v ->
+      unless ((() <$ v) == expected) (fail (show (expected, () <$ v)))
+
+number :: Number -> Value ()
+number = Number ()
+
+atom :: Atom -> Value ()
+atom = Atom ()
+
+list :: [Value ()] -> Value ()
+list = List ()
+
+text :: Text -> Value ()
+text = Text ()
+
+sections :: [(Text, Value ())] -> Value ()
+sections xs = Sections () [Section () k v | (k,v) <- xs]
+
+main :: IO ()
+main = sequenceA_
+
+  [ parseTest (number (MkNumber (Radix10 0) 42))     ["42"]
+  , parseTest (number (MkNumber (Radix10 56) 42))    ["42e56"]
+  , parseTest (number (MkNumber (Radix10 56) 42.34)) ["42.34e56"]
+  , parseTest (number (MkNumber (Radix10 0) 42.34))  ["42.34"]
+  , parseTest (number (MkNumber (Radix10 0) 42))     ["42."]
+  , parseTest (number (MkNumber (Radix10 0) 42))     ["042"]
+
+  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x2a"]
+  , parseTest (number (MkNumber (Radix16 56) 42))    ["0x2ap56"]
+  , parseTest (number (MkNumber (Radix16 56) (0x2a + (0x34 / 16^(2::Int))))) ["0x2a.34p56"]
+  , parseTest (number (MkNumber (Radix16 0)  (0x2a + (0x3f / 16^(2::Int))))) ["0x2a.3f"]
+  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x2a."]
+  , parseTest (number (MkNumber (Radix16 0) 42))     ["0x02a"]
+
+  , parseTest (number (MkNumber Radix2 42))     ["0b101010"]
+  , parseTest (number (MkNumber Radix2 4))     ["0b0100"]
+  , parseTest (number (MkNumber Radix2 4))     ["0b0100."]
+  , parseTest (number (MkNumber Radix2 (4 + (22 / 2^(6::Int))))) ["0b100.010110"]
+
+  , parseTest (number (MkNumber Radix8 55))     ["0o67"]
+  , parseTest (number (MkNumber Radix8 55))     ["0o67."]
+  , parseTest (number (MkNumber Radix8 55))     ["0o067"]
+  , parseTest (number (MkNumber Radix8 (55 + (10 / 64)))) ["0o67.12"]
+
+  , parseTest (atom "example") ["example"]
+  , parseTest (atom "one-two") ["one-two"]
+  , parseTest (atom "one-1") ["one-1"]
+
+  , parseTest (list []) ["[ ]"]
+  , parseTest (list []) ["[]"]
+  , parseTest (list [atom "x"]) ["[x]"]
+  , parseTest (list [atom "x"]) ["* x"]
+  , parseTest (list [atom "x", atom "y", atom "z"]) ["[x, y, z]"]
+  , parseTest (list [atom "x", atom "y", atom "z"])
+        ["* x", "* y", "* z"]
+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["[x,[y,z]]"]
+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["* x", "* [y,z]"]
+  , parseTest (list [atom "x", list [atom "y", atom "z"]]) ["* x", "* * y", "  * z"]
+
+  , parseTest (text "string") ["\"string\""]
+  , parseTest (text "\10string\1\2") ["\"\\x0ast\\&r\\     \\ing\\SOH\\^B\""]
+  , parseTest (text "string") ["\"str\\", "     \\ing\""]
+
+  , parseTest (sections []) ["{}"]
+  , parseTest (sections [("x", atom "y")]) ["{x:y}"]
+  , parseTest (sections [("x", atom "y")]) ["x:y"]
+  , parseTest (sections [("x", atom "y"), ("z", atom "w")]) ["{x:y,z:w}"]
+  , parseTest (sections [("x", sections [("y", atom "z")])]) ["x:y:z"]
+  , parseTest (sections [("x", sections [("y", atom "z")])])
+      ["x:"
+      ," y:"
+      ,"  z"]
+  , parseTest (sections [("x", list [atom "y", atom "z"])])
+      ["x: * y"
+      ,"   * z"]
+  , parseTest (list [sections [("x", atom "y")], sections [("z", atom "w")]])
+      ["* x: y"
+      ,"* z: w"]
+  ]
