VC++ optimisations break comparisons with NaN?

c++, ieee-754, visual-c++, visual-studio-2008

Solution

So, to summarise, there were a number of separate issues:

the original example code yielded undefined behaviour by violating strict aliasing. Fixing this was sufficient to resolve the problem for some versions of the 32-bit compiler.

with that issue fixed, removing `/fp:fast` resolved the problem for all versions of both the 32-bit and 64-bit compilers available to me

Martinho mentions that the problem no longer exists in cl 16.0 even with `/fp:fast`

Problem

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

Original source