www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - FP equality tests

reply bearophile <bearophileHUGS lycos.com> writes:
I remember Don lot of time ago saying something about FP equality tests. And in
the last months I have learnt to take Don words seriously. Recently I have read
C coding standards that discourage such tests, and I have seen that a very
strict C coding standard even fully forbids to use == among FP values.

I have read a paper titled "The pitfalls of verifying floating-point
computations" by David Monniaux:
http://hal.archives-ouvertes.fr/docs/00/28/14/29/PDF/floating-point-article.pdf

This is a little program converted to D plus a quotation from it:

-----------

void doNothing(double* x) {}
void main() {
    double x = 0x1p-1022;
    double y = 0x1p100;
    double z = 0;
    doNothing(&y);
    z = x / y;
    if (z != 0) {
        doNothing(&z);
        assert(z != 0);
    }
}


This program exhibits different behaviours depending on various factors, even
when one uses the same compiler (gcc version 4.0.2 on IA32):
* If it is compiled without optimisation, x / y is computed as a long double
then converted into a IEEE-754 double precision number (0) in order to be saved
into memory variable z, which is then reloaded from memory for the test. The if
statement is thus not taken.
* If it is compiled as a single source code with optimisation, gcc performs
some kind of global analysis which understands that do_nothing does nothing.
Then, it does constant propagation, sees that z is 0, thus that the if
statement is not taken, and finally that main() performs no side effect. It
then effectively compiles main() as a "no operation".
* If it is compiled as two source codes (one for each function) with opti-
misation, gcc is not able to use information about what do_nothing() does when
compiling main(). It will thus generate two function calls to do_nothing(), and
will not assume that the value of y (respectively, z) is conserved across
do_nothing(&y) (respectively, do_nothing(&z)). The z != 0 test is performed on
a nonzero long double quantity and thus the test is taken. However, after the
do_nothing(&z) function call, z is reloaded from main memory as the value 0
(because conversion to double-precision flushed it to 0). As a consequence, the
final assertion fails, somehow contrary to what many programmers would expect.
* With the same compilation setup as the last case, removing do_nothing(&z)
results in the assertion being true: z is then not flushed to memory and thus
kept as an extended precision nonzero floating-point value.
One should therefore be extra careful with strict comparisons, [...]

-----------

Sometimes I use C/D code like this:

double x = 0.0;
// ... code that may modify x
if (x == 0.0) { ...

So I test x to have the original exact value I have put inside it, but this
kind of code is not so safe.

Having == among FP values in C isn't a strong enough justification. What are
the use cases to keep allowing == between built-in FP values in D2?

Bye,
bearophile
Jan 17 2011
parent so <so so.do> writes:
 double x = 0.0;
 // ... code that may modify x
 if (x == 0.0) { ...

 So I test x to have the original exact value I have put inside it, but  
 this kind of code is not so safe.

 Having == among FP values in C isn't a strong enough justification. What  
 are the use cases to keep allowing == between built-in FP values in D2?

It is perfectly safe if you are aware of what you are asking. We can't just judge it here with two lines, hard to see your intention. Say you have a precomputed value and after some point you want to check if it is the value you are after, you need an exact comparison here. Problem is using floating point without knowing that it is not accurate, and expecting mathematically correct results. Even if we know it, there are still unsolved problems with it. High quality collision detection and response is one example.
Jan 18 2011