I want to implement the equivalent of C's uint
-to-double
cast in the GHC Haskell compiler. We already implement int
-to-double
using FILD
or CVTSI2SD
. Is there unsigned versions of these operations or am I supposed to zero out the highest bit of the uint
before the conversion (thus losing range)?
You can exploit some of the properties of the IEEE double format and interpret the unsigned value as part of the mantissa, while adding some carefully crafted exponent.
Bits 63 62-52 51-0
S Exp Mantissa
0 1075 20 bits 0, followed by your unsigned int
The 1075 comes from the IEEE exponent bias (1023) for doubles and a "shift" amount of 52 bits for your mantissa. Note that there is a implicit "1" leading the mantissa, which needs to be subtracted later.
So:
double uint32_to_double(uint32_t x) {
uint64_t xx = x;
xx += 1075ULL << 52; // add the exponent
double d = *(double*)&xx; // or use a union to convert
return d - (1ULL << 52); // 2 ^^ 52
}
If you don't have native 64 bit on you platform a version using SSE for the integer steps might be beneficial, but that depends of course.
On my platform this compiles to
0000000000000000 <uint32_to_double>:
0: 48 b8 00 00 00 00 00 movabs $0x4330000000000000,%rax
7: 00 30 43
a: 89 ff mov %edi,%edi
c: 48 01 f8 add %rdi,%rax
f: c4 e1 f9 6e c0 vmovq %rax,%xmm0
14: c5 fb 5c 05 00 00 00 vsubsd 0x0(%rip),%xmm0,%xmm0
1b: 00
1c: c3 retq
which looks pretty good. The 0x0(%rip)
is the magic double constant, and if inlined some instructions like the upper 32 bit zeroing and the constant reload will vanish.