Thread overview
Float values are wrong in union
Aug 22, 2016
stunaep
Aug 22, 2016
jkpl
Aug 22, 2016
Cauterite
Aug 22, 2016
jkpl
Aug 22, 2016
Engine Machine
Aug 29, 2016
Basile B.
August 22, 2016
I made a union to convert between int bits and floats, but the values are coming out wrong sometimes. This is working without issue in other languages so I'm really stumped. Here's an example:

>union test { int i; float f; }
>test t = { i : 0x7fb00000};
>float t2 = t.f;//int bits 0x7fb00000 as float
>test t3 = { f : t2 };
>writefln("%x", t3.i);//prints 7ff00000 NOT 0x7fb00000
August 22, 2016
On Monday, 22 August 2016 at 04:37:50 UTC, stunaep wrote:
> I made a union to convert between int bits and floats, but the values are coming out wrong sometimes. This is working without issue in other languages so I'm really stumped. Here's an example:
>
>>union test { int i; float f; }
>>test t = { i : 0x7fb00000};
>>float t2 = t.f;//int bits 0x7fb00000 as float
>>test t3 = { f : t2 };
>>writefln("%x", t3.i);//prints 7ff00000 NOT 0x7fb00000

Ok on linux, 0x7fb00000 is written, I tested under linux x86_64 with latest dmd beta, ldc and also gdc.

Which compiler and version do you use ?
Which OS and archi ?
August 22, 2016
On Monday, 22 August 2016 at 04:37:50 UTC, stunaep wrote:
> I made a union to convert between int bits and floats, but the values are coming out wrong sometimes.

I can already tell what this is going to be...
The problem is almost certainly nothing to do with your union, it's this line:
> float t2 = t.f;
This will load 0x7fb00000 into ST(0), which instantly converts it to 7FF00000 because it's a signalling NaN, then store ST(0) in your float `t2`.

Signalling NaNs are an ongoing problem in D's codegen. See Don's remarks at this page: https://issues.dlang.org/show_bug.cgi?id=16105#c2

The reason it works in other languages is because they don't place floats in the floating point registers for non-arithmetic operations. I've been trying to patch DMD's backend to behave this way too, but it's much harder than I expected (difficult codebase to understand/navigate).
August 22, 2016
On Monday, 22 August 2016 at 04:52:40 UTC, Cauterite wrote:
> On Monday, 22 August 2016 at 04:37:50 UTC, stunaep wrote:
>> I made a union to convert between int bits and floats, but the values are coming out wrong sometimes.
>
> I can already tell what this is going to be...
> The problem is almost certainly nothing to do with your union, it's this line:
>> float t2 = t.f;
> This will load 0x7fb00000 into ST(0), which instantly converts it to 7FF00000 because it's a signalling NaN, then store ST(0) in your float `t2`.
>
> Signalling NaNs are an ongoing problem in D's codegen. See Don's remarks at this page: https://issues.dlang.org/show_bug.cgi?id=16105#c2
>
> The reason it works in other languages is because they don't place floats in the floating point registers for non-arithmetic operations. I've been trying to patch DMD's backend to behave this way too, but it's much harder than I expected (difficult codebase to understand/navigate).

That's a 32 bit codegen issue then because DMD64 's disasm shows that SSE regs are used:

====
void foo()
{
    union test { int i; float f; }
    test t = { i : 0x7fb00000};
    float t2 = t.f;
    test t3 = { f : t2 };
}
===

yields to

===
00000000004586D0h  push rbp
00000000004586D1h  mov rbp, rsp
00000000004586D4h  sub rsp, 10h
00000000004586D8h  mov dword ptr [rbp-10h], 7FB00000h
00000000004586DFh  movss xmm0, dword ptr [rbp-10h]
00000000004586E4h  movss dword ptr [rbp-0Ch], xmm0
00000000004586E9h  movss xmm1, dword ptr [rbp-0Ch]
00000000004586EEh  movss dword ptr [rbp-08h], xmm1
00000000004586F3h  leave
00000000004586F4h  ret
===
August 22, 2016
On Monday, 22 August 2016 at 05:02:41 UTC, jkpl wrote:
> On Monday, 22 August 2016 at 04:52:40 UTC, Cauterite wrote:
>> [...]
>
> That's a 32 bit codegen issue then because DMD64 's disasm shows that SSE regs are used:
>
> ====
> void foo()
> {
>     union test { int i; float f; }
>     test t = { i : 0x7fb00000};
>     float t2 = t.f;
>     test t3 = { f : t2 };
> }
> ===
>
> yields to
>
> ===
> 00000000004586D0h  push rbp
> 00000000004586D1h  mov rbp, rsp
> 00000000004586D4h  sub rsp, 10h
> 00000000004586D8h  mov dword ptr [rbp-10h], 7FB00000h
> 00000000004586DFh  movss xmm0, dword ptr [rbp-10h]
> 00000000004586E4h  movss dword ptr [rbp-0Ch], xmm0
> 00000000004586E9h  movss xmm1, dword ptr [rbp-0Ch]
> 00000000004586EEh  movss dword ptr [rbp-08h], xmm1
> 00000000004586F3h  leave
> 00000000004586F4h  ret
> ===

x86 give 7FF and x64 gives 7FB in win.
August 29, 2016
On Monday, 22 August 2016 at 18:19:52 UTC, Engine Machine wrote:
> On Monday, 22 August 2016 at 05:02:41 UTC, jkpl wrote:
>> On Monday, 22 August 2016 at 04:52:40 UTC, Cauterite wrote:
>>> [...]
>>
>> That's a 32 bit codegen issue then because DMD64 's disasm shows that SSE regs are used:
> x86 give 7FF and x64 gives 7FB in win.

You can hack the ABI this way:

    void loadInScratchReg(float[1] f...) {}

and pass a single float value.
However when you'll start to use the param (f[0]), loading in ST(0) will happen so you must write in iasm. (Not to mention an aggressive optimizer that would be able, I think, to replace the param type if no iasm is present).

example, usage of a tagged union to perform a safe bit cast:

https://github.com/BBasile/iz/blob/master/import/iz/sugar.d#L1176

Not tested yet...