www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Float values are wrong in union

reply stunaep <admin pea2nuts.com> writes:
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
Aug 21 2016
next sibling parent jkpl <jkpl nowhere.de> writes:
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 ?
Aug 21 2016
prev sibling parent reply Cauterite <cauterite gmail.com> writes:
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).
Aug 21 2016
parent reply jkpl <jkpl nowhere.de> writes:
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 ===
Aug 21 2016
parent reply Engine Machine <EM EM.com> writes:
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.
Aug 22 2016
parent Basile B. <b2.temp gmx.com> writes:
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...
Aug 29 2016