9

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

moonshadow
  • 86,889
  • 7
  • 82
  • 122
  • 1
    I suspect it may have something to do with the undefined behaviour caused by treating an `int` object as a `float`. Have you tried `0.0 / 0.0` instead? – Joseph Mansfield Apr 03 '13 at 12:01
  • 3
    The QNaN function has undefined behaviour. Have you tried [using the C++ facilities](http://en.cppreference.com/w/cpp/types/numeric_limits/quiet_NaN) to get a NaN instead? – R. Martinho Fernandes Apr 03 '13 at 12:02
  • That's a total red herring, I'm afraid (the original problem I encountered was rather more complex than this simple example I narrowed it down to). I have edited the sample to use the standard library function suggested in comments instead, the results are unchanged. – moonshadow Apr 03 '13 at 12:05
  • That's not a NaN anyway, because IEEE requires a NaN's significant to be non-zero. (Note: OP's original code used a reinterpret cast on 0x7fa00000) – Euro Micelli Apr 03 '13 at 12:10
  • 3
    I can reproduce this in cl 15.0, but it appears fixed in cl 16.0. – R. Martinho Fernandes Apr 03 '13 at 12:14
  • @Martinho: awesome - good to know I can put /fp:fast back we go to that – moonshadow Apr 03 '13 at 12:19

4 Answers4

5

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

moonshadow
  • 86,889
  • 7
  • 82
  • 122
4

You're invoking undefined behaviour by casting an int* to float*. I've tried your code with VS 2010, using std::numeric_limits<float>::quiet_NaN() instead of the cast, and it gave the expected result (all but the last one were false) with /O2 and /fp:fast.

UPDATE

I've copy-pasted your revised example into both VS 2010 and VS 2005. In both of these, the 32-bit compiler produces correct results (F F F F F T), while the 64-bit compiler does not.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Hm. What compiler version? I attach one example set of build commands that reproduces the incorrect behavior for me with std::numeric_limits::quiet_NaN() – moonshadow Apr 03 '13 at 12:12
  • 1
    Hm. Using the library function, *and* omitting /fp:fast, yields correct behaviour for all cases (32-bit and 64-bit builds, all optimisation levels) here. I'll go with that :) – moonshadow Apr 03 '13 at 12:17
  • 2
    @moonshadow why don't you post that as an answer instead of accepting one that doesn't solve your problem? – R. Martinho Fernandes Apr 03 '13 at 12:17
4

That's because the QNaN function invokes UB by violating strict aliasing. The VS compiler is well within its rights to produce any behaviour.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Puppy
  • 144,682
  • 38
  • 256
  • 465
0

I believe you are looking for the /fp:strict option.

You may also want the float_control pragma.

All this was right in the documentation under "Compiler options by category"

Ben
  • 34,935
  • 6
  • 74
  • 113
  • The issue has been resolved above, but for the record, none of `/fp:strict`, `/fp:precise`, or any of the pragmas help here. – moonshadow Apr 03 '13 at 15:39
  • @moonshadow, it's not clear from the comment trail above: "I have edited the sample to use the standard library function suggested in comments instead, the results are unchanged". So it seems the issue isn't resolved. Did you try `/fp:strict` with your previous `QNan()` function? Just trying to clarify. – Ben Apr 03 '13 at 21:58