[c++] 왜 Intel C ++ 컴파일러에서 NaN-NaN == 0.0입니까?

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: %f\nsub: %f %f %f\nadd: %f %f\ndiv: %f %f %f\nmul: %f %f\n",
        neg, sub1,sub2,sub3, add1,add2, div1,div2,div3, mul1,mul2
    );

    return 0;
}

이 예제 ( live live here )는 기본적으로 내가 기대하는 것을 생성합니다 (부정은 조금 이상하지만 다소 의미가 있습니다).

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)은 이에 대해 무엇을 말하고 있으며 컴파일러간에 동작에 차이가있는 이유는 무엇입니까?



답변

인텔 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. 0 “x x가 NaN이거나 무한대 인 경우 x x x 및 0. 0 식은 동일하지 않습니다.”

(여기서 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의 불이행 선택을 비판하는 것이 전적으로 공평하다고 생각 하지만, 그 결정에 대한 유닉스 전쟁 전체를 다시 읽지는 않을 것입니다.


답변