为什么NaN - NaN == 0.0与Intel c++编译器

Why does NaN - NaN == 0.0 with the Intel C++ Compiler?

本文关键字:NaN c++ 编译器 Intel 为什么      更新时间:2023-10-16

大家都知道nan是通过算术传播的,但是我找不到任何证明,所以我写了一个小测试:

#include <limits>
#include <cstdio>
int main(int argc, char* argv[]) {
    float qNaN = std::numeric_limits<float>::quiet_NaN();
    float neg = -qNaN;
    float sub1 = 6.0f - qNaN;
    float sub2 = qNaN - 6.0f;
    float sub3 = qNaN - qNaN;
    float add1 = 6.0f + qNaN;
    float add2 = qNaN + qNaN;
    float div1 = 6.0f / qNaN;
    float div2 = qNaN / 6.0f;
    float div3 = qNaN / qNaN;
    float mul1 = 6.0f * qNaN;
    float mul2 = qNaN * qNaN;
    printf(
        "neg: %fnsub: %f %f %fnadd: %f %fndiv: %f %f %fnmul: %f %fn",
        neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
    );
    return 0;
}

这个例子(在这里运行)基本上产生了我所期望的(负号有点奇怪,但它有点意义):

neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

MSVC 2015产生了类似的东西。然而,Intel c++ 15产生:

neg: -nan(ind)
sub: nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

具体来说,是qNaN - qNaN == 0.0 .

…不可能是对的,对吧?相关标准(ISO C, ISO c++, IEEE 754)对此有何规定?为什么在编译器之间存在行为差异?

Intel c++编译器中的默认浮点处理是/fp:fast,它不安全地处理NaN(这也导致NaN == NaNtrue)。尝试指定/fp:strict/fp:precise,看看是否有帮助。

这个…不可能是对的,对吧?我的问题是:相关标准(ISO C、ISO c++、IEEE 754)对此有何规定?

Petr Abdulin已经回答了为什么编译器给出了0.0的答案。

以下是IEEE-754:2008的规定:

(6.2 nan操作)"[…]对于具有安静NaN输入的操作,除最大值和最小值操作外,如果要传递浮点结果,则结果应是一个应该是输入NaN之一的安静NaN。"

所以两个安静NaN操作数相减的唯一有效结果是一个安静NaN;其他结果无效

C标准说:

(C11, F.9.2表达式转换p1) "[…]

x−x→0。表达式x - x和0。如果x是NaN或infinite"

(此处NaN表示F.2.1p1中的安静NaN;此规范未定义信令NaN的行为。它通常使用术语NaN来表示安静的NaN

既然我看到了质疑英特尔编译器的标准遵从性的答案,而且没有其他人提到过这一点,我将指出GCC和Clang都有一种模式,它们做的事情非常相似。它们的默认行为是符合ieee -

$ g++ -O2 test.cc && ./a.out 
neg: -nan
sub: nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan
$ clang++ -O2 test.cc && ./a.out 
neg: -nan
sub: -nan nan nan
add: nan nan
div: nan nan nan
mul: nan nan

-但是如果你以牺牲正确性为代价来要求速度,你会得到你想要的-

$ g++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: nan nan 0.000000
add: nan nan
div: nan nan 1.000000
mul: nan nan
$ clang++ -O2 -ffast-math test.cc && ./a.out 
neg: -nan
sub: -nan nan 0.000000
add: nan nan
div: nan nan nan
mul: nan nan

我认为批评ICC选择default是完全公平的,但我不会把整个Unix战争读回那个决定。