関連するIEEE規格は、数値定数NaN(数値ではない)を定義し、NaNがそれ自体と等しくないと比較するように規定しています。何故ですか?
私が使い慣れているすべての言語がこのルールを実装しています。しかし、NaNがコンテナに格納されているとき、NaNが並べ替えられているデータにあるときなど、予期しない動作など、重大な問題を引き起こすことがよくあります。 NaNについて学ぶ前に)、驚くべきことにバグと混乱を増します。
IEEE規格はよく考えられているので、NaNがそれ自体と同等であると比較するのが悪いのには十分な理由があると確信しています。それが何なのかわかりません。
私の元の答え(4年前から)は、決定が行われた背景を理解することなく、現代の観点から決定を批判しています。そのため、質問には答えません。
正解は here :
NaN
!=NaN
は、2つの実用的な考慮事項から生まれました。[...] NaNが8087算術で形式化された時点では、
isnan( )
述語はありませんでした。プログラミング言語に依存しないNaN値を検出する便利で効率的な手段をプログラマに提供する必要がありましたが、isnan( )
のようなものは何年もかかる可能性があります。
このアプローチには1つの欠点がありました。数値計算とは関係のない多くの状況でNaNの有用性が低くなりました。たとえば、後になって人々がNaN
を使用して欠損値を表し、それらをハッシュベースのコンテナに入れたいと思ったとき、それを行うことができませんでした。
委員会が将来のユースケースを予測し、それらを十分に重要であると考えた場合、NaN
のテストとして_x!=x
_の代わりに、より冗長な!(x<x & x>x)
を使用することができます。しかし、彼らの焦点はより実用的で狭いものでした。数値計算に最適なソリューションを提供し、そのため、彼らのアプローチに問題はありませんでした。
===
元の回答:
申し訳ありませんが、トップ投票の答えになった考えに感謝しているので、私はそれに反対します。 NaNは「未定義」を意味しません- http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF 、7ページを参照してください(「未定義」という単語を検索)。その文書が確認するように、NaNは明確に定義された概念です。
さらに、IEEEのアプローチは、可能な限り通常の数学規則に従うことであり、できなかった場合は、「最も驚き」という規則に従います- https://stackoverflow.com/a/1573715/336527を参照してください 。数学的なオブジェクトはそれ自体に等しいため、数学のルールは、NaN == NaNがTrueであることを意味します。私はそのような主要な数学的原理から逸脱する正当で強力な理由を見ることができません(比較の三分法などの重要性の低いルールは言うまでもありません)。
その結果、私の結論は次のとおりです。
IEEE委員会のメンバーはこれを非常に明確に考えていなかったため、間違いを犯しました。 IEEE委員会のアプローチを理解したり、標準がNaNについて正確に述べていることを気にする人はほとんどいないため(つまり、ほとんどのコンパイラのNaNの処理はIEEE標準に違反しています)、誰も警告を発しませんでした。したがって、この間違いは現在標準に組み込まれています。このような修正は既存のコードの多くを破壊するため、修正される可能性は低いです。
編集: ここに1つの投稿があります 非常に有益な議論から。注:公平なビューを得るには、スレッド全体を読む必要があります。Guidoは他のコア開発者とは異なるビューをとるからです。ただし、Guidoはこのトピックに個人的に興味はなく、主にTim Petersの推奨に従います。 _NaN != NaN
_を支持するTim Petersの議論がある人は、コメントに追加してください。彼らは私の意見を変えるチャンスがあります。
受け入れられた答えは、間違いなく100%です。途中で間違っていたり、わずかに間違っていたりすることはありません。この問題が、検索でこの質問が浮かび上がってくると、長い間、この問題がプログラマを混乱させ、誤解させるのではないかと心配しています。
NaNはすべての計算を介して伝播し、ウイルスのように感染するように設計されています。そのため、複雑で複雑な計算のどこかでNaNにぶつかっても、一見賢明な答えをバブルアウトすることはありません。それ以外の場合、アイデンティティにより、NaN/NaNは1に等しくなり、(NaN/NaN)== 1、(NaN * 1)== NaNなどの他のすべての結果も同様になります。ゼロ分母、NaNの生成など)を実行すると、計算結果から結果が大幅に不正確(または、さらに微妙に不正確)になる場合があります。その理由は明らかではありません。
また、数学関数の値を調べるときの計算でNaNを使用することには、十分な理由があります。リンクされたドキュメントに記載されている例の1つは、関数f()のzeros()を見つけることです。推測値を使用して関数をプローブするプロセスで、関数f()が理にかなった結果をもたらさないものをプローブすることができます。これにより、zeros()がNaNおよび作業を続けます。
NaNの代替手段は、不正な操作(シグナルまたはトラップとも呼ばれます)が検出されるとすぐに例外をトリガーすることです。発生する可能性のある大きなパフォーマンスペナルティに加えて、当時はCPUがハードウェアでサポートするか、OS /言語がソフトウェアでサポートするという保証はありませんでした。誰もが浮動小数点を処理する独自のスノーフレークでした。 IEEEは、これをソフトウェアでNaN値として明示的に処理することで、OSまたはプログラミング言語間で移植できるようにしました。通常、正しい浮動小数点アルゴリズムは、node.jsであろうとCOBOLであろうと(hah)、すべての浮動小数点実装で正しいです。
理論的には、特定の#pragmaディレクティブを設定したり、クレイジーなコンパイラフラグを設定したり、正しい例外をキャッチしたり、特別なシグナルハンドラーをインストールして、同一のアルゴリズムに見えるものを実際に正しく動作させる必要はありません。残念ながら、一部の言語設計者とコンパイラ作成者は、この機能を最大限に元に戻すのに忙しくしています。
IEEE 754浮動小数点の歴史に関する情報の一部をお読みください。また、委員会のメンバーが回答した同様の質問に関するこの回答: IEEE754 NaN値に対してfalseを返すすべての比較の根拠は何ですか?
まあ、log(-1)
はNaN
を与え、acos(2)
はNaN
も与えます。それはlog(-1) == acos(2)
ということですか?明らかにない。したがって、NaN
がそれ自体と等しくないことは完全に理にかなっています。
ほぼ2年後にこれを再考するために、「NaNセーフ」比較関数を次に示します。
function compare(a,b) {
return a == b || (isNaN(a) && isNaN(b));
}
素敵なプロパティは次のとおりです。ifx == x
はfalseを返し、x
はNaN.
(このプロパティを使用して、x
がNaN
であるかどうかを確認できます。)
これを試して:
var a = 'asdf';
var b = null;
var intA = parseInt(a);
var intB = parseInt(b);
console.log(intA); //logs NaN
console.log(intB); //logs NaN
console.log(intA==intB);// logs false
IntA == intBが真の場合、a == bであると結論付ける可能性がありますが、明らかにそうではありません。
別の見方をすれば、NaNは、何かではなく、何かが何かについての情報を提供するだけです。たとえば、「an Appleはゴリラではありません」、「オレンジはゴリラではありません」と言う場合、「リンゴ」==「オレンジ」と結論付けますか?
実際、数学には「統一」値として知られる概念があります。これらの値は、システムの範囲外の問題を調整するために慎重に構築された拡張機能です。たとえば、複雑な平面の無限遠のリングを1つのポイントまたは一連のポイントと考えることができます。これまでは大げさな問題はなくなりました。セットのカーディナリティに関する他の例があり、| P(A)|である限り無限の連続体の構造を選択できることを実証できます。 > | A |そして何も壊れません。
免責事項:私は数学の勉強中にいくつかの興味深い警告の漠然とした記憶でのみ作業しています。上記で暗示した概念を表現するというひどい仕事をした場合、私は謝罪します。
NaNが単独の値であると信じたい場合、おそらく等式演算子が期待どおりに動作しない/期待するような結果の一部に不満を感じるでしょう。ただし、NaNが孤立したプレースホルダーで表される「悪」の連続体であると信じることを選択した場合、等値演算子の動作に完全に満足します。言い換えれば、あなたはあなたが海で捕まえた魚を見失うが、あなたは同じように見えるが同じように臭いがする別のものを捕まえる。