Pythonで0.1 + 0.2 が 0.3 にならない理由
浮動小数点数は2進数で正確に表現できない値があり、計算結果にわずかな誤差が生じます。==での比較はほぼ確実に失敗します。
なぜエラーが出ないのか
出力: False
(エラーなし)- Pythonは何も報告しません。文法として正しいためです
出力: False- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 print(0.1 + 0.2 == 0.3 ) ^
0.1や0.2は2進数では正確に表現できず、わずかな誤差を含みます。浮動小数点数の比較は==ではなく、差が十分小さいかどうかで判定します。
直し方: 0.1 + 0.2 == 0.3 を abs(0.1 + 0.2 - 0.3) < 1e-9 にします。
パターン2
1 total = 0.1 + 0.1 + 0.1 2 print(total == 0.3 ) ^
累積計算では誤差がさらに積み重なります。金額計算などで==比較をすると、理論上正しいはずの値が一致しないことがあります。
直し方: total == 0.3 を abs(total - 0.3) < 1e-9 にします。
パターン3
1 x = 1.1 - 1.0 2 print(x == 0.1 ) ^
引き算でも同じ誤差が発生します。金額や単位換算などfloatを使う場面では、常に許容誤差を使った比較を検討します。
直し方: x == 0.1 を abs(x - 0.1) < 1e-9 にします。
パターン4
1 price = 2.1 - 2.0 2 print(price == 0.1 ) ^
引き算の結果でも同じ誤差が出ます。価格の差額を求めるような場面でも==比較は避けます。
直し方: price == 0.1 を abs(price - 0.1) < 1e-9 にします。
パターン5
1 value = 0.7 - 0.1 - 0.2 2 print(value == 0.4 ) ^
計算を重ねるほど誤差も蓄積します。複数回の引き算・足し算を経た値の比較では特に注意が必要です。
直し方: value == 0.4 を abs(value - 0.4) < 1e-9 にします。
よくある誤解
floatをより精度の高い型に変えても解決しません。2進小数で表現できないという根本的な制約は変わらないため、比較は常に許容誤差(イプシロン)を使って行います。
実務での勘所
自分で誤差の許容範囲(イプシロン)を決めて比較するのは簡単そうに見えて、絶対誤差と相対誤差のどちらを使うべきかで落とし穴があります。Python 3.5以降はmath.isclose(a, b)という標準ライブラリの関数が用意されており、相対誤差と絶対誤差の両方を考慮した比較を自分で実装せずに使えます。自前でイプシロン比較を書く前に、まずこの関数で足りないか検討する価値があります。