1. ホーム
  2. floating-point

[解決済み】IEEE754のNaN値に対して、すべての比較がfalseを返す根拠は何ですか?

2022-03-27 01:38:07

質問

NaN 値の比較は、なぜ他のすべての値とは異なる動作をするのですか? つまり、演算子 ==, <=, >=, <, > で、片方または両方の値がNaNである場合の比較はすべて、他のすべての値の場合の動作とは逆に、falseを返します。

これは、何らかの形で数値計算を簡略化するためだと思いますが、その理由を明示的に示したものは IEEE754の現状に関するレクチャーノート は、他の設計上の決定について詳細に議論しています。

この逸脱した動作が、簡単なデータ処理を行う際にトラブルを引き起こしている。例えば、C言語で実数値のフィールドを持つレコードのリストをソートする場合、最大要素としてNaNを扱うための特別なコードを書く必要があり、さもなければソートアルゴリズムが混乱する可能性があります。

編集する これまでの回答はすべて、NaNを比較することは無意味であることを主張しています。

私もそう思うのですが、だからといって正解が偽というわけではありません。 幸いなことに、それはNaB(Not-a-Boolean)であり、存在しないのです。

ですから、比較のためにtrueを返すかfalseを返すかは、私の考えでは恣意的なものです。 一般的なデータ処理では、通常の法則に従った方が有利になります。 (の反射性、<, ==, >の三項対立)。 これらの法則に依存するデータ構造が混乱しないように。

だから、哲学的な理由だけでなく、これらの法則を破ることで得られる具体的な利点を教えてほしいのです。

2を編集します。 NaNを最大にすると、上限値の計算がめちゃくちゃになるので、良くないという理由がわかった気がします。

NaN != NaN は、次のようなループで収束を検出するのを避けるために望ましいかもしれません。

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

しかし、これは小さな限界との差の絶対値を比較することによって書かれるべきものです。 というわけで、NaNで反射性を壊す論拠としては、比較的弱いものだと思います。

どのように解決するのか?

私はIEEE-754委員会のメンバーでしたので、少しは物事を明確にするのに役立つと思います。

まず、浮動小数点数は実数ではないので、浮動小数点演算は実数演算の公理を満たしません。 浮動小数点数で成立しない実数演算の性質は三項対立だけではありませんし、最も重要なものでもありません。 例えば

  • 足し算は連想されない。
  • 分配法則は成り立たない。
  • 逆数のない浮動小数点数は存在する。

まだまだ続きますよ。 を満たす固定サイズの算術型を指定することはできない。 すべて 私たちが知っている実数演算の特性のうち 754委員会は、そのうちのいくつかを曲げるか壊すかを決めなければならない。 これは、非常にシンプルな原則によって導かれています。

  1. 可能な限り、実際の算術の動作に合わせます。
  2. それができないときは、できるだけ違反が予測でき、診断がしやすいようにします。

正解がfalseであることを意味するものではない」というコメントですが、これは間違いです。 述語の (y < x)y よりも小さい x . もし y がNaNである場合、それは ない 浮動小数点数より小さい x ということで、答えは必然的に偽になります。

浮動小数点値では三分法が成立しないことを述べました。 しかし、似たような性質で成立するものがあります。 754-2008規格の5.11項、2項です。

小なり、等しい、大なり、順不同の4つの互いに排他的な関係が可能である。最後のケースは、少なくとも一つのオペランドがNaNである場合に発生する。すべてのNaNは、それ自身を含むすべてのものと順不同に比較されなければならない。

NaNを処理するために余分なコードを書くことについては、通常はNaNが適切に処理されるようにコードを構成することが可能ですが(必ずしも容易ではありません)、常にそうであるとは限りません。 そうでない場合、余分なコードが必要になることもありますが、それは代数的クロージャが浮動小数点演算にもたらした利便性の代償としては小さいものです。


追記です。 多くのコメントで、NaN != NaNを採用しても身近な公理は保存されないようだという理由で、等式と三項対立の反射性を保存する方が有用だと主張されています。 私はこの意見に共感しているので、この回答を再検討し、もう少し文脈を説明しようと思う。

私の理解では、NaN != NaNは、2つの実用的な考慮から生まれたと思う。

  • それは x == y と同等であるべきです。 x - y == 0 可能な限り(実数演算の定理であることを超えて、これは比較のハードウェア実装をよりスペース効率的にするもので、標準が開発された当時は最も重要なことでした - ただし、これは x = y = infinity に対して違反するので、それだけでは大きな理由にはなりません。 (x - y == 0) or (x and y are both NaN) ).

  • さらに重要なのは、そこには isnan( ) 述語は、8087の演算でNaNが定式化された時点で、プログラミング言語が提供するものに依存しない、NaN値を検出する便利で効率的な手段をプログラマに提供する必要がありました。 isnan( ) これは何年もかかるかもしれない。 この件に関するカーチャン自身の文章を引用しておこう。

<ブロッククオート

もしNaNを取り除く方法がなかったら、CRAYのIndefiniteと同じように役に立たないでしょう。NaNに遭遇した時点で、Indefiniteの結論まで不定時間続けるよりも、計算を停止するのが最善でしょう。そのため、NaNに対するいくつかの演算はNaNでない結果を出さなければならないのです。どのような操作か?例外はC言語の述語「x == x」と「x != x」で、それぞれ無限または有限の数xに対して1と0になりますが、xが数でない(NaN)場合は逆になります。これらは、NaNを表す単語やIsNaN(x)述語を持たない言語で、唯一単純で例外のないNaNと数の区別を提供するものです。

これは、「Not-A-Boolean」のようなものを返すことを排除する論理でもあることに注意してください。 このプラグマティズムは見当違いだったのかもしれない。 isnan( ) しかし、そうなるとプログラミング言語の採用を待つ間の数年間は、NaNを効率的かつ便利に使うことがほぼ不可能になってしまう。 しかし、それでは、プログラミング言語の普及を待つ間の数年間、NaNを効率的かつ便利に使うことができなくなってしまう。それが合理的なトレードオフであったとは、私には思えない。

ぶっちゃけ、NaN==NaNという結果は今更変わらない。 ネットで文句を言うより、それで我慢することを覚えたほうがいい。 コンテナに適した順序関係を主張したいのであれば また が存在する場合、私はあなたの好きなプログラミング言語に totalOrder IEEE-754(2008)で標準化された述語です。 まだそうなっていないという事実が、現状の動機となったKahanの懸念の正当性を物語っているのです。