如果任一参数为 NaN,啥会导致 C/C++ <、<= 和 == 运算符返回 true?

Posted

技术标签:

【中文标题】如果任一参数为 NaN,啥会导致 C/C++ <、<= 和 == 运算符返回 true?【英文标题】:What would cause the C/C++ <, <=, and == operators to return true if either argument is NaN?如果任一参数为 NaN,什么会导致 C/C++ <、<= 和 == 运算符返回 true? 【发布时间】:2014-07-01 17:38:58 【问题描述】:

我对 IEEE-754 浮点比较规则的理解是,除了 != 之外的所有比较运算符,如果其中一个或两个参数都是 NaN,则将返回 false,而 != 运算符将返回 true。我可以通过简单的独立测试轻松重现此行为:

for (int ii = 0; ii < 4; ++ii)

    float a = (ii & 1) != 0 ? NAN : 1.0f;
    float b = (ii & 2) != 0 ? NAN : 2.0f;
    #define TEST(OP) printf("%4.1f %2s %4.1f => %s\n", a, #OP, b, a OP b ? "true" : "false");
    TEST(<)
    TEST(>)
    TEST(<=)
    TEST(>=)
    TEST(==)
    TEST(!=)

这会打印出预期的结果:(NaN 在 MSVC 运行时被格式化为 -1.$

 1.0  <  2.0 => true
 1.0  >  2.0 => false
 1.0 <=  2.0 => true
 1.0 >=  2.0 => false
 1.0 ==  2.0 => false
 1.0 !=  2.0 => true
-1.$  <  2.0 => false
-1.$  >  2.0 => false
-1.$ <=  2.0 => false
-1.$ >=  2.0 => false
-1.$ ==  2.0 => false
-1.$ !=  2.0 => true
 1.0  < -1.$ => false
 1.0  > -1.$ => false
 1.0 <= -1.$ => false
 1.0 >= -1.$ => false
 1.0 == -1.$ => false
 1.0 != -1.$ => true
-1.$  < -1.$ => false
-1.$  > -1.$ => false
-1.$ <= -1.$ => false
-1.$ >= -1.$ => false
-1.$ == -1.$ => false
-1.$ != -1.$ => true

但是,当我将这段代码粘贴到应用程序的内部循环的深处时,所有浮点计算都在其中执行,我得到了这些莫名其妙的结果:

 1.0  <  2.0 => true
 1.0  >  2.0 => false
 1.0 <=  2.0 => true
 1.0 >=  2.0 => false
 1.0 ==  2.0 => false
 1.0 !=  2.0 => true
-1.$  <  2.0 => true
-1.$  >  2.0 => false
-1.$ <=  2.0 => true
-1.$ >=  2.0 => false
-1.$ ==  2.0 => true
-1.$ !=  2.0 => false
 1.0  < -1.$ => true
 1.0  > -1.$ => false
 1.0 <= -1.$ => true
 1.0 >= -1.$ => false
 1.0 == -1.$ => true
 1.0 != -1.$ => false
-1.$  < -1.$ => true
-1.$  > -1.$ => false
-1.$ <= -1.$ => true
-1.$ >= -1.$ => false
-1.$ == -1.$ => true
-1.$ != -1.$ => false

由于某种原因,&lt;&lt;=== 运算符在其中一个或两个参数为 NaN 时意外返回 true。此外,!= 运算符意外返回 false。

这是 64 位代码,使用 Visual Studio 2010 构建,在 Intel Xeon E5-2650 上运行。使用_mm_getcsr(),我已确认 CSR 寄存器在两种情况下都保持相同的值。

还有什么可以像这样影响浮点数学的行为?

【问题讨论】:

我讨厌只提供 Dilbert 的名言,但“这是五分钱,孩子。让自己成为一个更好的编译器” 您确定他们的传统准 C89 模式被宣传为符合 IEEE-754?无论如何,您是否启用了快速数学或类似功能? 似乎你的编译器为了性能而抛出了规范的某些部分...... 似乎编译器假设它可以通过假设 a = b 相反来节省一条比较指令。没关系,在这种情况下它会产生无意义的结果。 @gnasher729:这是一个很好的假设。我怀疑 sean 的应用程序的内部循环是在 VS 等效的 -ffinite-math 下编译的,这将允许这种行为。 【参考方案1】:

此行为是由于/fp:fast MSVC 编译器选项造成的,该选项(除其他外)允许编译器执行比较而不考虑正确的 NaN 行为,以生成更快的代码。使用 /fp:precise/fp:strict 会导致这些比较在出现 NaN 参数时按预期进行。

【讨论】:

+1 并添加了链接。请记住,您也可以使用 #pragma float_control 为特定代码部分设置此行为。 奇怪的是,/fp:fast 选项只会在更大的应用程序上下文中触发这种无效的 NaN 行为。当我在独立的main() 函数中将/fp:fast 应用于此代码时,它的行为正确。 谢谢@BillyONEal,这正是我所需要的。很少有代码需要严格的 NaN 处理。

以上是关于如果任一参数为 NaN,啥会导致 C/C++ <、<= 和 == 运算符返回 true?的主要内容,如果未能解决你的问题,请参考以下文章

当检查为 0 时,啥会导致外键异常?

virtualenvwrapper 安装失败 - “啥会导致 ImportError:没有名为核心的模块”等?

尝试访问本地文件时,啥会导致 curl 返回 false?

MSMQ:从队列接收时,啥会导致“资源不足以执行操作”错误?

在 windbg 中,啥会导致消息“警告:无法验证 mydll.dll 的时间戳”?

在 os.system() 期间,啥会导致“IOError: [Errno 9] Bad file descriptor”?