digitalmars.D - FP equality tests
- bearophile (32/32) Jan 17 2011 I remember Don lot of time ago saying something about FP equality tests....
- so (8/15) Jan 18 2011 It is perfectly safe if you are aware of what you are asking.
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
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