いくつかのフロートをデコードして、実際に何が起こっているか見てみましょう! Common Lisp を使用します。Common Lisp には、浮動小数点数の有効桁数 (別名仮数) と指数を取得するための便利な関数があり、ビットをいじる必要はありません。使用されるすべての浮動小数点数は、IEEE 倍精度浮動小数点数です。
> (integer-decode-float 1.0d0)
4503599627370496
-52
1
つまり、有意桁に格納されている値を整数と見なすと、使用可能な最大 2 のべき乗 (4503599627370496 = 2^52) を縮小 (2^-52) したものになります。(仮数の左側にゼロを持たない方が簡単なため、指数が 0 の 1 として格納されません。これにより、左端の 1 ビットの表現をスキップして精度を上げることができます。この形式ではない数値は呼び出されます。デノーマル。)
1e16 を見てみましょう。
> (integer-decode-float 1d16)
5000000000000000
1
1
ここに、(5000000000000000) * 2^1 という表現があります。有効桁数は丸められた 10 進数ですが、2 のべき乗ではないことに注意してください。これは、1e16 が 2 の累乗ではないためです。10 を掛けるたびに、2 と 5 を掛けていることになります。2 を掛けることは指数を増やすだけですが、5 を掛けることは「実際の」掛け算であり、ここでは 5 を 16 回掛けています。
5000000000000000 = 10001110000110111100100110111111000001000000000000000 (base 2)
double float には 53 ビットの有意桁があるため、これが 53 ビットの 2 進数であることに注意してください。
しかし、この状況を理解するための鍵は、指数が 1 であることです (指数が小さいということは、精度の限界に近づいていることを示しています)。これは、float 値がこの仮数の 2^1 = 2 倍であることを意味します。
では、この数に 1 を足すとどうなるでしょうか? さて、同じスケールで 1 を表す必要があります。しかし、仮数の最下位ビットの値が 2 であるため、この数値に加えることができる最小の変更は正確に 2 です。
つまり、仮数をインクリメントして可能な限り小さな変更を行うと、次のようになります。
5000000000000001 = 10001110000110111100100110111111000001000000000000001 (base 2)
指数を適用すると、2 * 5000000000000001 = 1000000000000002 が得られます。これは、観察した値とまったく同じです。10000000000000000 または 10000000000000002 のいずれかのみを持つことができ、10000000000000001.1 は後者に近いです。
(ここでの問題は、10 進数が 2 進数で正確ではないということでさえないことに注意してください! ここには 2 進数の「10 進数の繰り返し」はなく、有効桁数の右端にたくさんの 0 ビットがあります。入力がきちんと落ちるだけです。最下位ビットのすぐ上。)