a==b と a-b==0 は同じか?

この文章は、2007-05-24に対するトラックバック的なコメントです。

この「==」の実装が適当なのかどうかの議論ですが、適当不適当と言うより非常に不思議だと僕は感じます。というのも、異なる浮動小数点数の差が0になることはありえないはずですから、Inf、-Inf、NaN以外の数について言えば、差が0かどうかを調べるのと==で比較するのとは何も変わりません。

「異なる浮動小数点数の差が0になることはありえない」という言葉にひっかかりました。hnwさんは先刻ご存知のことかもしれませんが、「異なる浮動小数点数の差」について考えてみたいと思います。

一般的には、異なる浮動小数点数の差が0になることはありえます。それはアンダーフローと呼ばれる状況の場合です。
浮動小数点は、扱える絶対値に範囲があります。演算の結果の絶対値がそれ未満になる場合は、通常ゼロに丸められ、有効数字が失われます。これをアンダーフローといいます。
話を簡単にするため、10進の浮動小数点数で説明しましょう。以下のような形式を考えます。

± 0.DDDDD × 10^±nn

D, n:0-9の一桁の数値
^ :べき乗記号(10^2は10の2乗)
± :符号(プラスあるいはマイナス)

ここで、先頭のDのみ、値の範囲を 1〜9 とします。このような決め事を正規化といいます。0.DDDDDの部分を仮数部、±nnの部分を指数部といいます。
上記の形式で表現できる正の値は、

0.10000 × 10^-99 〜 0.99999 × 10^99

となります。
アンダーフローが発生するケースの典型例は、絶対値が極めて小さい数値同士の減算です。たとえば、以下のような場合

0.10001 × 10^-99 - 0.1 × 10^-99

この結果は、 0.00001 × 10^-99 すなわち、0.1 × 10^-104 となり、正規化表現ができなくなります。これがアンダーフローであり、通常は 0 に丸められます。
すなわち、このようなケースでは、「異なる浮動小数点数の差が0になる」わけです。

ところが、我々が普段使っているIEEE754規格の浮動小数点数では、「非正規化数」というものを認めています。上記の例でいうと、0.00001 × 10^-99 という表現を認めてしまうわけです。

# この例では10進で説明していますが、私たちが普段使っている double は、2進の浮動小数点数です。

非正規化浮動小数点数では、指数部が最小値より小さくなった場合、指数部を最小値(上記例では-99)固定にして、仮数部を柔軟に(正規化せずに)対応します。つまり、この部分では固定小数点数のような挙動を示します。固定小数点数であれば、a==b と、a-b==0は同値ですから、この現象を納得しやすいかもしれません(却って分かりにくいか?)。
非正規化数は、IEEE754によって普及した手法ですから、同規格の普及前は、「異なる浮動小数点数の差が0になることはありえ」たわけです。

きっと、hnwさんは上記のようなことは先刻ご承知なのでしょう。ですが、これは非正規化数を認めた場合であって、一般の場合には必ずしもそうではないことを補足しておいた方が良いと思い、あえて突っ込みました。