Under certain circumstances, comparing a normal floating point number with std::numeric_limits<float>::quiet_NaN()
always yields "true"
when compiled with GCC + "-ffast-math"
(tested on Linux and MinGW). On other circumstances it always yields "false"
(which would be the IEEE compliant behavior).
If one of the values is known to be a quiet_NaN
at compile time, then the behavior is IEEE compliant and branches get optimized away.
I know that "-ffast-math"
allows to break IEEE rules and that it implies "-ffinite-math-only"
which assumes that there are no NaNs. But even with "-ffast-math"
the result of quiet_NaN()
has a specific bit pattern (e.g. 7FF80000), so how can a comparison with a normal float like 0.5f
possibly yield true?
Here's a code sample showing the different cases. Please compile with "g++ -O3 -ffast-math"
.
#include <iostream>
#include <limits>
int main() {
#if 1
// make sure that the value of 'x' is not known at compile time
bool isnan;
std::cout << "use nan (0|1): ";
std::cin >> isnan;
#else
// otherwise GCC correctly applies IEEE rules for NaN and the branch below is optimized away accordingly
bool isnan = true;
#endif
float x = isnan ? std::numeric_limits<float>::quiet_NaN() : 0.5f;
std::cout << "x: " << x << std::endl;
float a;
std::cout << "type a float: ";
std::cin >> a;
#if 1
// *always* prints 1 (!)
std::cout << a << " equal to " << x << ": " << (x == a) << std::endl;
#else
// always prints false - the opposite from above!
std::cout << a << " equal to " << x << ": " << ((x == a) ? "true" : "false") << std::endl;
#endif
return 0;
}
also, here's a godbolt link.
I guess the relevant part is at line 66 (with "-ffast-math"
) resp. 67 (without "-ffast-math"
). Can someone explain to me the difference between those instructions?
Is this behavior of GCC acceptable or should I file a bug report?
EDIT: I want to make clear that I don't need to know whether a particular number is a NaN (I know that this is unspecified with "-ffast-math"), I'm only interested whether two numbers are (not) equal. In my actual code, I have a cache of floating point values and I perform update operations only if the input is different than the cached value. I've initialized the cache with quite NaNs, so they won't compare equal to any normal float and the first input is guaranteed to cause an update. This worked fine but as soon as I've added "-ffast-math", the check for newval != oldval
always returned false, so there would never be an update. I've seen this quiet_NaN pattern in the SuperCollider source code and found it quite elegant.