Haskell's read
is a bit too strict about floating point numbers:
$ ghci
GHCi, version 8.2.2: http://www.haskell.org/ghc/ :? for help
Prelude> read "-1E34" :: Double
-1.0e34
Prelude> read "-1.E34" :: Double
*** Exception: Prelude.read: no parse
Prelude>
Is there a version of read that accepts the second form? It is quite common in physical sciences. For example, Fortran reads and writes such forms.
Another example that Haskell doesn't support is ".1" for "0.1". This one is even more common. I just don't want to convert the input ascii file. . . .
Here's a custom parser that does this, using megaparsec.
import Text.Megaparsec
import Text.Megaparsec.Char
realLiteral :: (MonadParsec e s m, Token s ~ Char) => m Double
realLiteral = mkFloat <$> sign <*> intgPart <*> fracPart <*> exponent
where mkFloat sgn itg frc expn
= fromIntegral sgn * (fromIntegral itg + frc) * 10^^expn
sign = (-1) <$ char '-'
<|> 1 <$ char '+'
<|> pure 1
intgPart = read . ('0':) <$> many digitChar
fracPart = char '.' *> (toFrc<$>many digitChar)
<|> pure 0
where toFrc "" = 0
toFrc digits = read digits / 10^length digits
exponent = oneOf "eEdD" *> ((*) <$> sign <*> (read<$>some digitChar))
<|> pure 0
[1 of 1] Compiling Main ( wtmpf-file5764.hs, interpreted ) Ok, 1 module loaded. *Main> parseMaybe realLiteral "1" Just 1.0 *Main> parseMaybe realLiteral "-3" Just (-3.0) *Main> parseMaybe realLiteral "-9e+2" Just (-900.0) *Main> parseMaybe realLiteral ".3e+9" Just 3.0e8 *Main> parseMaybe realLiteral "-1.E34" Just (-1.0000000000000001e34) *Main> parseMaybe realLiteral "-1.673986e-40" Just (-1.6739859999999999e-40) *Main> parseMaybe realLiteral "-3.E+16" Just (-3.0e16)
As Somebody said, you can create a helper function (or three) to help transform the number into a format that works with read
. I'm not the best with Haskell, so I'm not sure what other solutions exist, but I wrote some functions to help. I've tested it with read
, and everything seems to work fine so far.
prefixZero :: String -> String
prefixZero "" = ""
prefixZero ('-' : xs) = '-' : '0' : xs
prefixZero s = '0' : s
suffixZero :: String -> String
suffixZero "" = ""
suffixZero ('.' : exp@('E' : _)) = '.' : '0' : exp
suffixZero (x : xs) = x : suffixZero xs
format :: String -> String
format = suffixZero . prefixZero
You can call the following:
read (format "-1.E34") :: Double
Which outputs the following:
-1.0e34
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With