IEEE 754 浮動小数点ビットパターン可視化ツール
10進数の小数をIEEE 754の単精度(32ビット)・倍精度(64ビット)ビットパターンに分解して可視化、またはビットパターンから10進数に逆変換します。符号・指数部・仮数部を色分け表示し、NaN・無限大・非正規化数も判別できます。
古典的な例: なぜ 0.1 + 0.2 は 0.3 にならないのか
JavaScriptで `0.1 + 0.2` を計算すると `0.30000000000000004` になり、`0.3` とは一致しません。これは倍精度浮動小数点数のビットパターンを見ると理由がわかります。
| 値 | 倍精度(64ビット)16進数 | 倍精度(64ビット)2進数 |
|---|---|---|
| 0.1 | 0x3FB999999999999A | 0011111110111001100110011001100110011001100110011001100110011010 |
| 0.2 | 0x3FC999999999999A | 0011111111001001100110011001100110011001100110011001100110011010 |
| 0.3 | 0x3FD3333333333333 | 0011111111010011001100110011001100110011001100110011001100110011 |
| 0.1 + 0.2 | 0x3FD3333333333334 | 0011111111010011001100110011001100110011001100110011001100110100 |
0.1も0.2も0.3も、2進数では無限小数(循環小数)になるため、有限の52ビットの仮数部には収まりきらず丸められます。0.1と0.2の丸め誤差の方向がたまたま一致しないため、両者を足した結果は0.3を正確に表すビットパターンとは1ビットだけ異なる値(0x3FD3333333333334)になります。上の変換ツールで実際にこれらの値を入力し、末尾ビットの違いを確認してみてください。
使い方のヒント
- 0.1・0.2・0.3を倍精度でそれぞれビット化し、0.1+0.2の結果(0.30000000000000004)と見比べると、末尾1ビットだけ異なることが視覚的に確認できます。
- 「-0」を10進数欄に入力すると、符号ビットだけが1になったビットパターン(+0とは異なる)が得られます。数値としては0===-0ですが、ビット表現は別物です。
- 指数部がすべて0のとき、仮数部が0ならゼロ、0でなければ非正規化数(精度を犠牲にしてゼロに極めて近い値を表す特殊な表現)になります。
- 単精度(32ビット)と倍精度(64ビット)を切り替えると、同じ0.1という値でも丸め誤差の大きさが異なる(単精度の方が誤差が大きい)ことが確認できます。
- ビットパターンからの逆変換では、`0x`接頭辞付きの16進数か、選択した精度と同じ桁数の2進数(`0b`接頭辞は任意)で入力する必要があります。
よくある質問
余談ですが ― IEEE 754という「共通言語」ができるまで
1985年以前、コンピューターメーカー各社は独自の浮動小数点数表現を採用していました。IBM・DEC・Crayなど、同じ「0.1」という値を計算しても、計算機が違えば結果の丸め方や表現できる範囲が微妙に異なり、あるマシンで書いたプログラムを別のマシンに移植すると数値計算の結果がわずかに食い違うという問題が常態化していました。この混乱を解消するため、Intel 8087数値演算コプロセッサの設計にも携わったカリフォルニア大学バークレー校のWilliam Kahanらが中心となり、業界横断の標準規格として1985年に「IEEE 754」が策定されました。
IEEE 754の核心的なアイデアは、有限のビット数(32ビットや64ビット)で無限に存在する実数を近似的に表現するにあたり、符号・指数部・仮数部という3つの区分に役割を分担させたことです。指数部が桁の大きさ(スケール)を担当し、仮数部が有効数字の精度を担当することで、非常に小さい数から非常に大きい数まで、限られたビット数でも柔軟に対応できる仕組みになっています。指数部にバイアス(単精度なら127、倍精度なら1023)を足して格納するのは、符号付き整数用の2の補数のような複雑な符号処理をせず、ビット列同士を単純比較するだけで大小関係が判定できるようにするための工夫です。
2進数で「きれいに」表現できる小数は、分母が2のべき乗になる分数(1/2、1/4、3/8など)に限られます。10進数の0.1は分数で書くと1/10ですが、10は2のべき乗ではないため、2進数では0.0001100110011...と無限に続く循環小数になってしまいます。有限桁の仮数部に収めるには途中で打ち切って丸めるほかなく、これが「0.1を正確にコンピューターで表現できない」根本的な理由です。10進数の0.3を10進数の世界で正確に扱いたい会計システムなどが、浮動小数点数ではなく整数演算や専用の10進数型(BCD、decimal型)を使うのはこのためです。
IEEE 754はその後、2008年・2019年に改訂され、半精度(16ビット)や四倍精度(128ビット)、10進浮動小数点数の規定なども追加されましたが、根幹となる符号・指数部・仮数部の3分割構造と丸めモードの考え方は1985年の初版から変わっていません。ほぼすべてのプログラミング言語のfloat/double型がこの規格に準拠しているため、「なぜ0.1+0.2が0.3にならないのか」という疑問は、言語を問わず共通して発生する現象なのです。