Skip to content

`word2Double#` does not match `doubleFromInteger` for large values

Summary

Implementation of word2Double# doesn't match the one from gmp-integer: doubleFromInteger for many values larger than 2^63

Steps to reproduce

Start up ghci:

>>> import GHC.Float
>>> word2Double maxBound
1.8446744073709552e19
>>> fromIntegral (maxBound :: Word) :: Double
1.844674407370955e19
>>> import GHC.Integer
>>> :set -XMagicHash
>>> word2Double maxBound == D# (doubleFromInteger (toInteger (maxBound :: Word64)))
False

Here are also three quickcheck properties that depict the problem:

      it "word2Double == doubleFromInteger (2^63 <= x < 2^64)" $
        property $
        forAll (choose (2 ^ 63, maxBound)) $ \(w :: Word) ->
          word2Double w === D# (doubleFromInteger (toInteger w))
      it "word2Double == doubleFromInteger (2^54 <= x <= 2^63)" $
        property $
        forAll (choose (2 ^ 54, 2 ^ 63)) $ \(w :: Word) ->
          word2Double w === D# (doubleFromInteger (toInteger w))
      it "int2Double == doubleFromInteger (2^52 <= x < 2^63)" $
        property $
        forAll (choose (2 ^ 54, maxBound)) $ \(w :: Int) ->
          int2Double w === D# (doubleFromInteger (toInteger w))

First one fails consistently, while the other two I have not seen to fail yet.

Expected behavior

  • Expect word2Double a to produce exactly the same result as \(a :: Word64) -> D# (doubleFromInteger (toInteger a)) for all a

This bug has some funny consequences. For example compiling with or without optimizations this:

round (fromIntegral (maxBound :: Word64) :: Double) :: Word64

will produce either 0 or 18446744073709549568 respectfully.

Environment

  • GHC version used: 8.8.2 (and I suspect current HEAD and all prior versions)

Optional:

  • Operating System: Ubuntu LTS 18.04
  • System Architecture: x86_64
Edited by Alexey Kuleshevich
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information