浮動小数点数(IEEE 754)
10 進の数が、符号・指数部・仮数部のビット列にどう丸められて保存されるかを見せます。半精度・単精度・倍精度などの形式と丸めの向きを変えて、0.1 + 0.2 の誤差や桁落ち、非正規化数・∞・NaN を確かめられます。
binary32。C の float、GPU のシェーダー、JavaScript の Math.fround・Float32Array など。10 進でおよそ 7 桁の精度です。(符号 1・指数 8・仮数 23 ビット)
一番近い値に丸め、ちょうど中間なら仮数の最後のビットが 0(偶数)のほうを選びます。ふつうの計算はすべてこれです。
10 進では短い 0.1 も、2 進では 0.0001100110011… と無限に続くので、途中で丸めた近い値が保存されます。
ビット列
ビットを押すと 0 と 1 が入れ替わります。左が最上位ビットです。
符号(1 ビット)
指数部(8 ビット)
仮数部(23 ビット)
ビット列の読み方
- 符号
- 0 → 正(+)
- 指数部
- 01111011(2 進) → 123 − バイアス 127 = -4
- 仮数部
- 1.10011001100110011001101(2 進) = 1.600000023841858
(−1)0 × 1.600000023841858 × 2-4 = 0.1
保存される値
- 種類
- 正規化数
- 正確な値
- 0.100000001490116119384765625
- 最短の表記
- 0.1(読み直すと同じビット列に戻る一番短い 10 進)
- ULP(隣との間隔)
- 7.451e-9
- 入力とのずれ
- 1.490e-9相対 1.490e-80.2 ULP
周りの浮動小数点数
縦線が表せる値です。そのあいだの数は表せないので、入力した値は近くの線(丸めの向きに合う線)に丸められます。
四則演算
浮動小数点数の計算は「保存されている 2 つの値で正確に計算し、結果を 1 回だけ丸める」と決められています。入力した数のまま計算した数学の答えと比べます。
| ビット列 | 最短の表記 | 正確な値 | |
|---|---|---|---|
| 左の数 0.1 | 3DCCCCCD | 0.1 | 0.100000001490116119384765625 |
| 右の数 0.2 | 3E4CCCCD | 0.2 | 0.20000000298023223876953125 |
| 保存した値で正確に計算 | — | — | 0.300000004470348358154296875 |
| 結果 0.1 + 0.2 | 3E99999A | 0.3 | 0.300000011920928955078125 |
| 数学の答えを丸めた値 | 3E99999A | 0.3 | 0.300000011920928955078125 |
結果は数学の答え(3/10(≈ 0.3))を丸めた値と同じで、これ以上近い値はありません。
形式の比較
入力した値(0.1)を各形式で保存したときの値と、入力からのずれも並べます。
| 形式 | 指数 / 仮数 | バイアス | 最大の値 | 最小の正規化数 | 最小の非正規化数 | イプシロン | 10 進の桁 | 保存される値 | 相対誤差 |
|---|---|---|---|---|---|---|---|---|---|
| 8 ビット | 4 / 3 | 7 | 240 | 0.01563 | 0.001953 | 0.125 | 1.2 | 0.1 | 0.01563 |
| 半精度(16) | 5 / 10 | 15 | 65504 | 6.104e-5 | 5.960e-8 | 0.0009766 | 3.3 | 0.1 | 0.0002441 |
| bfloat16 | 8 / 7 | 127 | 3.390e38 | 1.175e-38 | 9.184e-41 | 0.007813 | 2.4 | 0.1 | 0.0009766 |
| 単精度(32) | 8 / 23 | 127 | 3.403e38 | 1.175e-38 | 1.401e-45 | 1.192e-7 | 7.2 | 0.1 | 1.490e-8 |
| 倍精度(64) | 11 / 52 | 1023 | 1.798e308 | 2.225e-308 | 4.941e-324 | 2.220e-16 | 16.0 | 0.1 | 5.551e-17 |
表せる数の並び(8 ビット)
8 ビットの形式で表せる 0 以上の有限の値(120 個)を数直線に並べます。指数が 1 増えるごとに間隔が 2 倍に広がるので、0 に近いほど詰まっています。短い線は非正規化数で、0 の近くを等しい間隔で埋めています。
0〜16 にある値: 89 個(0〜1 に 57 個、1 より大きい値は 63 個)
使い方
- 形式(8 ビット・半精度・bfloat16・単精度・倍精度)と、丸めの向きを選びます。
- 10 進の数(1.5・-2.5e-3)や分数(1/3)を入力すると、その形式で保存されるビット列と、丸めた正確な値・入力とのずれを表示します。16 進のビット列を直接入力することもできます。
- ビットを押すと 0 と 1 が入れ替わり、値がどう変わるかを確かめられます。「1 つ小さい値」「1 つ大きい値」で、隣の浮動小数点数に移れます。
- 「四則演算」では、2 つの数の計算結果と、数学の答えを丸めた値を比べます。例のボタンで 0.1 + 0.2 や桁落ちの例を呼び出せます。
浮動小数点数のしくみ
IEEE 754 の浮動小数点数は、数を「符号 × 1.xxx(2 進)× 2 の指数乗」の形で表し、符号・指数部・仮数部の 3 つのビットの並びに詰めます。正規化数では仮数の先頭は必ず 1 なので、そのビットは保存しません(けち表現)。指数部には、実際の指数にバイアス(単精度なら 127)を足した値を入れ、負の指数も 0 以上の整数で表します。
指数部がすべて 0 の値は非正規化数で、先頭の 1 を省かずに最小の指数のまま仮数を小さくしていき、0 との間をなめらかにつなぎます。指数部がすべて 1 の値は特別な値で、仮数部が 0 なら ∞、それ以外なら NaN(非数)です。0 にも符号があり、+0 と -0 は等しいとみなされますが、1 を割ると ±∞ に分かれます。
丸めと誤差
仮数のビット数には限りがあるので、表せる数は飛び飛びです。隣どうしの間隔を ULP(unit in the last place)と呼び、指数が 1 増えるごとに 2 倍に広がります。入力した数は表せる数のどれかに丸められ、最近接の丸めならずれは 0.5 ULP 以下です。10 進では短い 0.1 も 2 進では無限に続くため、正確には保存できません。
計算は、保存されている 2 つの値で正確に計算した結果を 1 回だけ丸めます。それでも 0.1 + 0.2 が 0.3 にならないのは、0.1 と 0.2 を保存したときの誤差が足し合わされるためです。大きさの違う数を足すと小さいほうが消える「情報落ち」や、近い数どうしを引くと誤差の割合が大きくなる「桁落ち」も、この丸めから起こります。
このアプリは BigInt を使った分数で正確な値を計算し、自前で丸めています。単精度と倍精度の結果は、ブラウザの Float32Array・number と同じビット列になります。