なぜインテル®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;
}

例(ここでライブ実行)は、基本的に私が期待するものを生成します(ネガティブは少し変ですが、それはある程度理にかなっています):

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

MSVC 2015でも同様のものが生成されます。ただし、インテル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も同様に動作します。
ポール

33
安全でない数学の最適化を有効にした可能性があります(-ffast-mathgcc の同等のもの)?
Matteo Italia

5
@nm:真実ではありません。指定された浮動小数点の挙動持つように支持され、必要に応じてオプションですが、規範的である附属書F、全てでは、基本的にCにIEEE 754を組み込んだ
R .. GitHubのSTOP手助けICE

5
IEEE 754標準について質問したい場合は、質問のどこかにそれを述べてください。
n。「代名詞」m。

68
この質問はタイトルのJavaScriptに関するものだと確信しました。
MikeTheLiar

回答:


300

インテルC ++コンパイラーでのデフォルトの浮動小数点処理は/fp:fastであり、これはをNaN安全に処理しません(これも原因とNaN == NaNなりtrueます)。指定する/fp:strict/fp:precise、それが役立つかどうかを確認してください。


15
自分で試してみました。実際、正確または厳密のいずれかを指定すると、問題が修正されます。
imallett 2015

67
Intelのデフォルトを決定することを支持したいと思い/fp:fastます。何か安全なものが必要場合は、最初からNaNが発生しないように==してください。通常、浮動小数点数では使用しないでください。IEEE754がNaNに割り当てる奇妙なセマンティクスに依存すると、問題が発生します。
leftaroundabout

10
@leftaroundabout:NaNを真に返すというIMHOの恐ろしい決定を除けば、NaNについて何がおかしいと思いますか?
スーパーキャット2015

21
NaNには重要な用途があります。NaNは計算のたびにテストを必要とせずに例外的な状況を検出できます。すべての浮動小数点開発者がそれらを必要とするわけではありませんが、無視しないでください。
Bruce Dawson

6
@supercat好奇心から、NaN==NaN返品するという決定に同意しfalseますか?
カイルストランド

53

この 。。。正しくありませんね 私の質問:関連する規格(ISO C、ISO C ++、IEEE 754)はこれについて何と言っていますか?

Petr Abdulinは、コンパイラが0.0答えを出す理由をすでに答えています。

IEEE-754:2008の内容は次のとおりです。

(6.2 NaNを使用した演算)「[...]最大および最小の演算以外の静かなNaN入力を伴う演算では、浮動小数点の結果が配信される場合、結果は静かなNaNとなり、 NaNを入力してください。」

したがって、2つのクワイエットNaNオペランドの減算の唯一の有効な結果は、クワイエットNaNです。その他の結果は無効です。

C標準は言う:

(C11、F.9.2式変換p1) "[...]

x − x→0. 0「式x − xと0. 0は、xがNaNまたは無限大の場合、同等ではありません。」

(ここで、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のデフォルトの選択を批判することは完全に公平だと思いますが、私はUnix戦争全体をその決定に戻すことはしません。


-ffast-mathでは、gcc浮動小数点演算に関して、がISO 9899:2011に準拠していないことに注意してください。
fuz

1
@FUZxxlはい、要点は、両方のコンパイラに非準拠の浮動小数点モードがあるということです。iccはデフォルトでそのモードになり、gccはそうではないというだけです。
zwol 2015

4
燃料を燃やすために、デフォルトで高速演算を有効にするというIntelの選択が本当に気に入っています。フロートを使用する重要な点は、高いスループットを得ることです。
Navin、2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.