on master help sign in
Parse `Scientific` directly, avoiding loss in precision.

For example, before: λ> Yaml.decode "x: 9.78159610558926e-5" :: Maybe Yaml.Value Just (Object (fromList [("x",Number 9.781596105589261e-5)])) after: λ> Yaml.decode "x: 9.78159610558926e-5" :: Maybe Yaml.Value Just (Object (fromList [("x",Number 9.78159610558926e-5)]))

commit621c89bb6de451c1b8a171e73ec57b20dc20f02e authorFrancesco Mazzoli <f@mazzo.li> dateThu 3 Sep 2015, 22:49 (11 years ago) parentf28149e
2 files changed+31−4
Data/Yaml/Internal.hs+8−4 test/Data/YamlSpec.hs+23
Data/Yaml/Internal.hs+8−4
@@ -37,10 +37,11 @@ import Data.Text.Encoding (decodeUtf8With)
3737import Data.Text.Encoding.Error (lenientDecode)
3838import qualified Data.HashMap.Strict as M
3939import Data.Typeable
40−import Data.Text.Read
4140#if MIN_VERSION_aeson(0, 7, 0)
42−import Data.Scientific (fromFloatDigits)
41+import qualified Data.Attoparsec.Text as Atto
42+import Data.Scientific (Scientific)
4343#else
44+import Data.Text.Read
4445import Data.Attoparsec.Number
4546#endif
4647import Control.Monad.Trans.Resource (ResourceT, runResourceT)
@@ -162,8 +163,7 @@ textToValue _ _ t
162163 | any (t `isLike`) ["y", "yes", "on", "true"] = Bool True
163164 | any (t `isLike`) ["n", "no", "off", "false"] = Bool False
164165#if MIN_VERSION_aeson(0, 7, 0)
165− | Right (x, "") <- signed decimal t = Number $ fromIntegral (x :: Integer)
166− | Right (x, "") <- double t = Number $ fromFloatDigits x
166+ | Right x <- textToScientific t = Number x
167167#else
168168 | Right (x, "") <- signed decimal t = Number $ I x
169169 | Right (x, "") <- double t = Number $ D x
@@ -172,6 +172,10 @@ textToValue _ _ t
172172 where x `isLike` ref = x `elem` [ref, T.toUpper ref, titleCased]
173173 where titleCased = toUpper (T.head ref) `T.cons` T.tail ref
174174
175+#if MIN_VERSION_aeson(0, 7, 0)
176+textToScientific :: Text -> Either String Scientific
177+textToScientific = Atto.parseOnly (Atto.scientific <* Atto.endOfInput)
178+#endif
175179
176180parseO :: C.Sink Event Parse Value
177181parseO = do
test/Data/YamlSpec.hs+23
@@ -148,6 +148,10 @@ spec = do
148148 it "serialization of +123 #64" $ do
149149 D.decode (D.encode ("+123" :: String)) `shouldBe` Just ("+123" :: String)
150150
151+#if MIN_VERSION_aeson(0, 7, 0)
152+ it "preserves Scientific precision" casePreservesScientificPrecision
153+#endif
154+
151155
152156specialStrings :: [T.Text]
153157specialStrings =
@@ -465,3 +469,22 @@ caseIssue49 =
465469 ])
466470 where
467471 src = "---\na:\n &id5 value: 1.0\nb:\n *id5: 1.2"
472+
473+#if MIN_VERSION_aeson(0, 7, 0)
474+-- | We cannot guarantee this before aeson started using 'Scientific'.
475+casePreservesScientificPrecision :: Assertion
476+casePreservesScientificPrecision = do
477+ D.decodeEither "x: 1e-100000" @?= Right (object
478+ [ "x" .= D.Number (read "1e-100000") ])
479+ -- Note that this ought to work also without 'Scientific', given
480+ -- that @read (show "9.78159610558926e-5") == 9.78159610558926e-5@.
481+ -- However, it didn't work (and still doesn't work with aeson < 0.7)
482+ -- for two reasons:
483+ --
484+ -- * We use 'Data.Text.Read.double', which is not as accurate as it
485+ -- can be;
486+ -- * Even if we used 'Data.Text.Read.rational' we would not get good
487+ -- results, because of <https://github.com/bos/text/issues/34>.
488+ D.decodeEither "x: 9.78159610558926e-5" @?= Right (object
489+ [ "x" .= D.Number (read "9.78159610558926e-5") ])
490+#endif