in reply to [Win32, C, and way OT] C floats, doubles, and their equivalence
The problem is routed in how v6, (pre-v8 maybe, but I only have v6 and v8), generates the code. The following 'fixes' the problem, though I realise that it may not be a workable solution for you:
#include <stdio.h> cmpFsFd( float s, double d ) { float tmp = (float)d; return s == tmp ? 1 : 0; } int main(void) { double nv = 2.0 / 3; float foo = 2.0 / 3; if( foo == nv ) printf("True "); else printf("False "); if( cmpFsFd( foo, nv )) printf("True\n"); else printf("False\n"); return 0; }
[19:48:23.40} C:\test>cl float.c /Fefloatv8.exe Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 +for 80x86 Copyright (C) Microsoft Corporation. All rights reserved. float.c Microsoft (R) Incremental Linker Version 9.00.30729.01 Copyright (C) Microsoft Corporation. All rights reserved. /out:floatv8.exe float.obj [19:49:59.60} C:\test>floatv8 False True ---------------------------------------------------- c:\test>cl float.c /Fefloatv6.exe Microsoft (R) 32-bit C/C++ Standard Compiler Version 13.00.9466 for 80 +x86 Copyright (C) Microsoft Corporation 1984-2001. All rights reserved. float.c Microsoft (R) Incremental Linker Version 7.00.9466 Copyright (C) Microsoft Corporation. All rights reserved. /out:floatv6.exe float.obj c:\test>floatv6 False True
Basically, when you coerce a double to a float before comparing it to a float, you need to force the compiler to store the coerced value as a float before doing the comparison. That's what my cmpFsFd() is doing. (Insert underscores to taste :)
The reasoning is that it is only when the values are stored to memory (moved out of the FP registers), that the actual rounding/truncation occurs. Whilst values remain within the FP registers they are maintained as 80-bit FP values, regardless of whether they originate as 32-bit or 64-bit FPs.
The v8 (and presumably other compilers) do the coercion ((float)nv), by storing and and reloading to a temporary 32-bit memory location:
; 15 : if( foo == (float)nv ) printf("True\n"); fld QWORD PTR _nv$[ebp] ## Load nv onto FPU stack fstp DWORD PTR tv79[ebp] ## store (and pop) it into a 32-bit ( +float) temporary fld DWORD PTR tv79[ebp] ## load it back onto the FPU stack fld DWORD PTR _foo$[ebp] ## load foo onto the FPU stack fucompp ## do the comparison fnstsw ax ## get the FPU status word into AX test ah, 68 ## 00000044H (Check for equality?) jp SHORT $LN2@main ## Jump push OFFSET $SG2485 ## or not ... call _printf
The equivalent code generated by the V6 compiler omits that store & load step:
; 15 : if( foo == (float)nv ) printf("True\n"); fld QWORD PTR _nv$[ebp] ## Load nv to FPU stack fst DWORD PTR tv78[ebp] ## Store it to a temporary but... *** NEVER LOADS IT BACK *** *** And does the comparison between the FPU register and the m +emory image of foo *** fcomp DWORD PTR _foo$[ebp] fnstsw ax test ah, 68 ; 00000044H jp SHORT $L800 push OFFSET FLAT:$SG801 call _printf
On the v7 compiler, you might get away with using /fp:strict or /fp:precise, but the v6 compiler lacks these options. (For the same reason, I haven't been able to check that theory!)
Maybe someone can come up with a preprocessor macro to map (float)x to something like ( float tmp = (float)d )? (Some of the macros in the Perl sources seem to do equally obscure things, but they fairly make my skin crawl :)
Personally, I'd prefer using cmpFsFd(), and perhaps an editor macro (with manual yea/nay) to change the sources. If the function was marked inline, it might not impose to much of a performance penalty, but you might have to be careful that the compiler doesn't optimise the tmp var away.
Anyway, I hope that is of some use to you.
Reference: http://webster.cs.ucr.edu/AoA/Windows/HTML/RealArithmetica2.html
11.2.5 Conversions The FPU performs all arithmetic operations on 80 bit real quantities. In a sense, the FLD and FST/FSTP instructions are conversion instructions as well as data movement instructions because they automatically convert between the internal 80 bit real format and the 32 and 64 bit memory formats.
|
|---|