0.1 + 0.2が0.3にならない理由
古典的な浮動小数点パズルの解説。IEEE 754で0.1 + 0.2が0.30000000000000004を生成する理由と、浮動小数点比較を正しく処理する方法を学びます。
Decimal Value
0.30000000000000004
Float32 Hex
0x3E99999A
Float64 Hex
0x3FD3333333333334
詳細な説明
0.1 + 0.2 !== 0.3という式は、最も有名な浮動小数点の驚きです。0.1、0.2、0.3はバイナリ浮動小数点で正確に表現できないため、事実上すべてのプログラミング言語で発生します。
0.1がバイナリで正確でない理由:
10進数の0.1はバイナリでは0.0001100110011...です — 10進数で1/3 = 0.333...と同様に無限に繰り返すパターンです。IEEE 754はこれを最も近い表現可能な値に丸める必要があります。
0.1に最も近いfloat64は:
0.1000000000000000055511151231257827021181583404541015625
0.2に最も近いfloat64は:
0.200000000000000011102230246251565404236316680908203125
加算:
格納された値を加算すると:
0.10000000000000000555... + 0.20000000000000001110... = 0.30000000000000004440...
この結果に最も近い表現可能なfloat64は:
0.3000000000000000444089209850062616169452667236328125
しかし0.3に最も近い表現可能なfloat64は:
0.299999999999999988897769753748434595763683319091796875
これらは異なる値なので、0.1 + 0.2 !== 0.3となります。
ビットレベルの表示:
| 値 | Float64 Hex |
|---|---|
| 0.1 | 0x3FB999999999999A |
| 0.2 | 0x3FC999999999999A |
| 0.1+0.2 | 0x3FD3333333333334 |
| 0.3 | 0x3FD3333333333333 |
0.1+0.2と0.3は最後の16進数桁が異なることに注目: 4 vs. 3。
浮動小数点の正しい比較方法:
- イプシロン比較: 1.0付近の値には
Math.abs(a - b) < Number.EPSILON - 相対イプシロン:
Math.abs(a - b) < Math.max(Math.abs(a), Math.abs(b)) * tolerance - 固定小数点: 通貨は100倍して(ドルでなくセントで計算)
- 10進数ライブラリ: 正確な10進算術には
decimal.jsやBigDecimalを使用
これはバグではありません:
この動作はIEEE 754に従った正しい結果です。10進法の分数を2進法で表現することに固有の問題です。同じ問題はC、C++、Java、Python、Rust、Goなど、IEEE 754浮動小数点を使用するすべての言語に存在します。
ユースケース
すべての開発者が、正しい数値比較を書き、丸め誤差のない金融計算を実装し、浮動小数点精度の問題をチームメイトやステークホルダーに説明するために、この動作を理解する必要があります。