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


300

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


18
Javascript와 Python (numpy)에는이 동작이 없습니다. Nan-NaN입니다 NaN. Perl과 Scala도 비슷하게 동작합니다.
Paul

33
안전하지 않은 수학 최적화 ( -ffast-mathgcc 와 동일)를 활성화했을 수 있습니까?
Matteo Italia 5

5
@nm : 사실이 아닙니다. 지정된 부동 소수점 동작하도록 지원하고, 필요한 경우 선택 사항이지만 규범이다 부록 F, 전혀은 본질적으로 C.에 IEEE 754를 통합
R .. GitHub의 STOP 돕기 ICE

5
IEEE 754 표준에 대해 문의하려면 질문 어딘가에 언급하십시오.
n. '대명사'm.

68
나는 확신했다 이 질문이 제목에서 JavaScript에 관한 것이라고 했다.
MikeTheLiar

답변:


300

인텔 C ++ 컴파일러의 기본 부동 소수점 처리는 /fp:fast 어떤 핸들 NaN의 안전하지 (에도있는 결과 NaN == NaNtrue예를 들면). 지정 시도 /fp:strict/fp:precise하고 도움이되는지 확인합니다.


15
나는 이것을 직접 시도했다. 실제로, 정확하거나 엄격하게 지정하면 문제가 해결됩니다.
imallett

67
나는 기본적으로 인텔의 결정을지지하고 싶습니다 /fp:fast: 당신이 뭔가 원하는 경우 안전을 , 당신은 아마 더 나은 피하기 NaN을 사용하지 않는 일반적으로 처음에 온, 그리고해야 ==부동 소수점 숫자. IEEE754가 NaN에 할당하는 이상한 의미에 의존하는 것이 문제를 요구하고 있습니다.
leftaroundabout

10
@leftaroundabout : NaN! = NaN이 true를 반환한다는 IMHO의 끔찍한 결정 외에 NaN에 대해 이상한 점이 무엇입니까?
supercat

21
NaN은 중요한 용도를 가지고 있습니다. 매 계산 후 테스트 없이도 예외 상황을 감지 할 수 있습니다. 모든 부동 소수점 개발자가 필요하지는 않지만 해제하지는 않습니다.
브루스 도슨

6
@supercat 호기심에 NaN==NaN귀환 하기로 결정한 것에 동의 false하십니까?
Kyle Strand

53

이 . . . 맞지 않아요? 내 질문 : 관련 표준 (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을 나타냅니다")


20

인텔 컴파일러의 표준 준수를 부정하는 답변을 보았으므로 아무도 언급하지 않았으므로 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의 불이행 선택을 비판하는 것이 전적으로 공평하다고 생각 하지만, 그 결정에 대한 유닉스 전쟁 전체를 다시 읽지는 않을 것입니다.


공지 사항에 그 -ffast-math, gccISO 9899에 부합되지 않습니다 더 이상 부동 소수점 연산에 대한 2011.
fuz September

1
@FUZxxl 예, 요점은 컴파일러 모두 호환되지 않는 부동 소수점 모드를 가지고 있다는 것입니다. icc의 기본값은 해당 모드이고 gcc는 그렇지 않습니다.
zwol

4
불을 피우기 위해 기본적으로 빠른 수학을 가능하게하는 인텔의 선택이 정말 좋습니다. 부동 소수점을 사용하는 요점은 높은 처리량을 얻는 것입니다.
Navin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.