レッスン
理論 — 浮動小数点数688語
浮動小数点数は、整数に2のべき乗を掛け合わせたものです。式にすると(−1)s × 1.f × 2E−1023となります。この形式において、Eとfにそれぞれ何ビットを割り当てるかが決められています。そして、このビットの配分ひとつで、表現できる数の範囲と、二つの数をどこまで細かく区別できるかの両方が確定するのです。
各記号の意味
s- 符号ビットです。正であれば0、負であれば1になります。数値の中に組み込まれるのではなく独立して存在するため、この形式には+0と−0の両方があります。
E- 指数部です。専用の符号を持たなくても負の数を表せるよう、実際の値に1023を足した形で保持されます。これで数値の範囲が決まりますが、隣り合う値の間隔を決定するのもまた、この範囲なのです。
f- 仮数部です。正規化された数は必ず1から始まるため、その先頭の1は保存されず、それに続く52ビットが格納されます。この52ビットが精度のすべてであり、指数部がそこに精度を付け加えることはありません。
- 前提
- 対象の数が有限であり、かつ正規数であること。2−1022 を下回ると先頭の1が失われ、代わりに精度を手放すことで表現を続けます。その結果、倍精度における正の最小値は 4.94 × 10−324 となり、仮数部はわずか一ビットになります。
- 成り立たない場合
- 破綻するのは二箇所。どちらも実際のコードで起こります。別々の計算経路をたどった二つの値を
=で比較すると、必ず失敗します。経路が違えば丸められ方も違うからです。また、長い足し算は足す順序に左右されます。リストを先頭から足すのと末尾から足すのとでは合計が一致しません。一番小さな項から先に足していくことで、失われる情報を最小限に抑えられます。
全プロセスの詳細解説
-
ある決済システムでは、ポンド単位の金額をbinary64で保持しています。£0.10と£0.20がどちらも本来よりわずかに大きい値として保存されること、そしてそれらを足しても£0.30にはならないことを示してください。また、決済エンジニアがこの問題にどう対処しているかも説明してください。
-
0.1を二進法で表すと、0.0001のあとに1001が無限に続きます。十分の一を作るには分母に5が必要ですが、二進法の小数には2しかありません。そのため、この展開が終わることは決してないのです。
-
2進数で53桁が保持され、残りの桁は丸めて捨てられます。ここでは切り上げが行われるため、保持される値は0.1を5.55 × 10⁻¹⁸だけ上回ります。ページ上の丸めのステップでは、その方向を矢印で示しています。
-
0.2はまったく同じ53桁で指数が1つ大きいだけなので、誤差もきっちり2倍の1.11 × 10⁻¹⁷になります。ボックスに0.2を入力して、二つのエラーカードを比べてみてください。
-
この二つの保存された値を足し合わせると、0.3を挟む二つの倍精度浮動小数点数のちょうど中間に着地します。このように中間で並んだ場合は最後のビットが偶数になる方の隣接値が選ばれ、ここでは大きい方の値がそれに該当します。
-
その結果、返ってくる値は0.3000000000000000444089209850062616169452667236328125となり、その後のすべての計算にこの値が引き継がれます。
-
今度は0.1と0.2の代わりに、10と20を入力してみてください。どちらも2⁵³をはるかに下回る整数であり、誤差なく保持されます。したがって10 + 20は30となり、どこにも情報の欠落は生じません。
解答
金額はポンドではなくペンスのような最小単位の整数として保持します。2⁵³までのすべての整数は正確に表現できるため、計算も正確になります。あとに残る丸め処理は、自分自身で意図したタイミングに書き込むものだけとなり、どこで丸められたかをはっきりと指し示すことができます。ツールを使えば、この両面を十秒で確認できます。0.1を入力するとエラーカードはゼロになりませんが、10を入力すればゼロになります。
-