[Issue 20451] comparing identical floating points does not work on Win32 and FreeBSD32.

d-bugmail at puremagic.com d-bugmail at puremagic.com
Sun Dec 15 22:50:34 UTC 2019


https://issues.dlang.org/show_bug.cgi?id=20451

--- Comment #4 from jacob <look.at.me.pee.please at gmail.com> ---
(In reply to berni44 from comment #3)
> (In reply to jacob from comment #1)
> > Comparing floating point numbers with "==" is in itself an error.
> 
> I know. But the "==" was there first. I tried to fix the issue and when I
> found out, that the results where indeed the correct results and the
> (original) problem was because double was compared with real and real
> produces just better results, I added the cast. This worked for most
> machines...
> 
> > You shouldn't be comparing approximation of numbers to see if they are exactly
> > the same.
> 
> I agree, but it's not an approximation in this case. (Although I meanwhile
> solved the original stuff by using approxEqual with a large constant, so
> actually it's comparing for identity.)

It is an approximation. You are computing something using two different
methods. The result at runtime is also dependent on hardware. You change
settings in SSE and the FPU so that the same two inputs give different results.
It's all an approximation if you are using floating point numbers. Even if math
says others, it's different when you have to do computations in the real world
and can't use fractions to retain infinite precision.

> >     // can't just cast(double) here cause pow() returns value on the FPU
> >     // so the conversion never happens otherwise
> >     double a = pow(xd, neg2);
> >     assert(a == cast(double) (1 / (x * x)));
> 
> I don't understand that. xd is of type F=double and the result of pow() is
> "Unqual!F" which is also double. FPU or not, the result should behave like a
> double. When casting another value to double and the results have equal bit
> patterns, they should be compareable with ==.


Non-working:

CALL       __D3std4math__T3powTydTysZQlFNaNbNiNeydysZd     
FLD        qword ptr [DAT_004d2f88]                         
FUCOMPP


Working (cast(double)):

CALL       __D3std4math__T3powTydTysZQlFNaNbNiNeydysZd    
FSTP       qword ptr [ESP + 0x8]
FLD        qword ptr [ESP + 0x8]
FLD        qword ptr [DAT_004d2f88]                     
FUCOMPP


Do you see the difference? It's a hardware problem, and it's the reason why the
FPU isn't used anymore. The D and C/C++ convention is to return floating point
numbers on the FPU. This means the number stays as it's 80-bit representation
in the FPU. All the calculations were probably done in the FPU as well. 

The reason the code works that I posted, was because we explicitly told it to
store the value before comparing it. As you can see, it "FSTP" (stores) the
value in a qword (64-bits) before loading it and doing the comparison.

> > You are comparing different types, pow() returns real
> 
> If it does, this is the bug.

It's not really a bug. It's how the FPU operatores, all values on the FPU are
stored as 80-bit fp. You get situations like these cause of the difference in
precision. The SSE doesn't support 80-bit floats, and operations are done in
the same precision they are given in. If you do 32-bit floating point
operations, they stay in 32-bit (if that's how it was programmed). With the FPU
everything gets "upgraded" to 80-bits. You can't really avoid it unless you
don't use it, but I imagine Windows/FreeBSD 32-bit ABI probably requires it.
Though I guess it could be changed for D's ABI.

--


More information about the Digitalmars-d-bugs mailing list