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