IEEE754 requires NaNs to be unordered; less than, greater than, equal etc. should all return false when one or both operands are NaN.
The sample below yields the correct F F F F F T
as expected when compiled using g++ at all optimisation levels, and when compiled using VC++'s CL.exe (32-bit version 15.00.30729.01) with no optimisation arguments or any combination of /Od, /fp:fast, /arch:SSE.
However, when compiled with /O1 or /O2 (and any/no other optimisation arguments), T T F F F T
results, even with /Op also specified.
The 64-bit version of CL.exe yields many variations - T T F F F T
, T T T F F T
, T T T F F F
etc - depending on optimisation level and whether /fp:fast is specified, but as with the 32-bit version, compliant behaviour only seems to be possible with all optimisation disabled.
Am I making some obvious mistake? Is there some way to cause the compiler to comply with standards here without sacrificing all other optimisation?
#include <limits>
#include <stdio.h>
int main( int argc, char const ** argv )
{
float test = std::numeric_limits<float>::quiet_NaN();
printf( "%c %c %c %c %c %c\n",
(test < test) ? 'T' : 'F',
(test <= test) ? 'T' : 'F',
(test == test) ? 'T' : 'F',
(test > test) ? 'T' : 'F',
(test >= test) ? 'T' : 'F',
(test != test) ? 'T' : 'F'
);
return 0;
}
An example build.cmd
that reproduces the problem:
set "PATH=c:\program files (x86)\Microsoft Visual Studio 9.0\VC\bin\amd64;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7;c:\program files (x86)\Microsoft Visual Studio 9.0\Common7\IDE"
set "LIB=c:\program files (x86)\microsoft visual studio 9.0\vc\lib\x64;c:\program files\Microsoft SDKs\Windows\v6.0A\Lib\x64"
cl test.cpp /fp:fast /Od /c /I "c:\program files (x86)\microsoft visual studio 9.0\vc\include"
link "/LIBPATH:C:/Program Files (x86)/Microsoft Visual Studio 9.0/vc/lib/amd64" "/LIBPATH:C:\Program Files\Microsoft SDKs\Windows\v6.0A\/Lib/x64" /DEBUG /IGNORE:4199 /IGNORE:4221 /MACHINE:X64 /SUBSYSTEM:CONSOLE test.obj
test
EDIT
For the record, the example originally given in the question used
inline float QNaN()
{
static int const QNaNValue = 0x7fc00000;
return *(reinterpret_cast<float const*>(&QNaNValue));
}
to generate a NaN; as a number of comments and answers point out, this is undefined behaviour, and replacing it with std::numeric_limits::quiet_NaN() actually fixed the issue for some versions of the 32-bit CL.exe