Skip to content

On x86_64, `castFloatToWord32` can generate bad, signed `Word32`s

Summary

On 64-bit architectures, when the primop stgFloatToWord32# is applied to a negative float (i.e., whose 32-bit representation has the sign bit set), it produces a sign-extended 64-bit word. As a result, castFloatToWord32 applied to negative floats will generate weird negative Word32s.

Steps to reproduce

Under GHCi:

> import GHC.Float
> castFloatToWord32 (-1)
-1082130432
> castFloatToWord32 (-1) < 0
False
> toInteger (castFloatToWord32 (-1)) < 0
True
>

Expected behavior

One would expect Word32s to stay positive, even in the face of such adversity:

> castFloatToWord32 (-1)
3212836864
> toInteger (castFloatToWord32 (-1)) < 0
False
>

Fix

The issue is that stg_floatToWord32zh in libraries/base/cbits/CastFloatWord.cmm uses:

w = TO_W_(I32[ptr])

where TO_W_ is %sx64 on 64-bit architectures. I'd suggest adding macros TO_ZW_ to Cmm.h for zero-extending analogues of TO_W_ and using that in stg_floatToWord32zh. (No one seems to be using %zx32 or %zx64 anywhere in the code base, but they might want to some day.)

I'd be happy to add a new test case and submit a patch.

Environment

  • GHC version used: 8.6.4 or latest HEAD
  • Operating System: Linux
  • System Architecture: x86_64
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information