VC ++优化打破了与NaN的比较?

时间:2013-04-03 11:52:41

标签: c++ visual-studio-2008 visual-c++ ieee-754

IEEE754要求NaNs无序;当一个或两个操作数都是NaN时,小于,大于,等等都应该返回false。

下面的示例在使用g ++在所有优化级别编译时,以及在使用VC ++的CL.exe(32位版本15.00.30729.01)编译时,没有优化参数或任何组合的情况下产生正确的F F F F F T。 / od,/ fp:fast,/ arch:SSE。

但是,当使用/ O1或/ O2(以及任何其他优化参数)编译时,T T F F F T结果,即使指定了/ Op也是如此。

64位版本的CL.exe产生了许多变体 - T T F F F TT T T F F TT T T F F F等 - 取决于优化级别以及是否指定了/ fp:fast,但是对于32位版本,只有在禁用所有优化的情况下才能实现符合规定的行为。

我犯了一些明显的错误吗?有没有办法让编译器在不牺牲所有其他优化的情况下遵守标准?

#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;
}

重现问题的示例build.cmd

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

修改

对于记录,最初在问题中给出的示例

inline float QNaN()
{
    static int const QNaNValue = 0x7fc00000;
    return *(reinterpret_cast<float const*>(&QNaNValue));
}

产生NaN;正如一些注释和答案所指出的,这是未定义的行为,并用std :: numeric_limits :: quiet_NaN()替换它实际上修复了某些版本的32位CL.exe的问题

4 个答案:

答案 0 :(得分:5)

总而言之,有许多不同的问题:

  • 原始示例代码违反了严格别名,从而产生了未定义的行为。修复此问题足以解决某些版本的32位编译器的问题。

  • 修复了该问题,删除/fp:fast解决了我可用的32位和64位编译器的所有版本的问题

  • Martinho提到即使使用/fp:fast

  • ,cl 16.0中也不再存在此问题

答案 1 :(得分:4)

您通过将int*强制转换为float*来调用未定义的行为。我已经使用std::numeric_limits<float>::quiet_NaN()而不是强制转换使用VS 2010尝试了您的代码,它使用false和{{1}给出了预期结果(除了最后一个/O2之外) }}

<强>更新

我已将修改后的示例复制粘贴到VS 2010和VS 2005中。在这两者中,32位编译器生成正确的结果(/fp:fast),而64位编译器则不生成。F F F F F T

答案 2 :(得分:4)

这是因为QNaN函数通过违反严格别名来调用UB。 VS编译器完全有权产生任何行为。

答案 3 :(得分:0)

我相信您正在寻找/fp:strict选项。

您可能还需要float_control pragma。

所有这些都在“按类别编译选项”

下的文档中是正确的