Search code examples
haskellfloating-pointparsec

Floating point numbers, precision, and Parsec


Consider the following code:

import Text.Parsec
import Text.Parsec.Language
import Text.Parsec.String
import qualified Text.Parsec.Token as Token

float :: Parser Double
float = Token.float (Token.makeTokenParser emptyDef)

myTest :: String -> Either ParseError Double
myTest = parse float ""

Now, thanks to QuickCheck I know a magic number (I have aligned result for convenience):

λ> myTest "4.23808622486133"
Right      4.2380862248613305

Some floating point numbers cannot be exactly represented in memory, some operations easily introduce «fluctuations» into floating point numbers. We all know that. However, cause of this parsing problem seems to be different.

A few words about tests that helped me discover this… feature. Put simply, in these tests floating point value is generated, printed, and parsed back (with Parsec). For example, number 9.2 is known to be impossible to represent as floating point value, however it passes the tests (obviously because of «smart» printing function). Why does 4.23808622486133 fail?


For those who believe that these numbers are the same and 4.23808622486133 is just shortest unambiguous representation of 4.2380862248613305:

a1 :: Double
a1 = 9.2000000000000003

a2 :: Double
a2 = 9.200000000000001

b1 :: Double
b1 = 4.23808622486133

b2 :: Double
b2 = 4.2380862248613305

Now:

λ> a1 == a2
True
λ> b1 == b2
False

Solution

  • This is still not fixed in Parsec. If this exact problem breaks your day, take a look at Megaparsec, which is a fork of Parsec that fixes many bugs and conceptual flaws, improves quality of error messages and more.

    As you can see this problem is fixed there:

    λ> parseTest float "4.23808622486133"
    4.23808622486133
    λ> parseTest float "4.2380862248613305"
    4.2380862248613305
    

    Disclosure: I'm one of the authors of Megaparsec.