FP equality tests

bearophile bearophileHUGS at lycos.com
Mon Jan 17 15:44:14 PST 2011


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


More information about the Digitalmars-d mailing list