0.1 + 0.2 === 0.3 がfalseになる理由と直し方
浮動小数点数は2進数で表現されるため、0.1や0.2のような10進の小数を正確に表せません。演算結果はごくわずかな誤差を含み、===での比較は失敗します。
なぜエラーが出ないのか
出力: false
(エラーなし)- JavaScriptは何も報告しません。文法として正しいためです
出力: false- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 const price = 0.1 + 0.2; 2 console.log(price === 0.3 ); ^
0.1と0.2は2進数の浮動小数点数として正確に表せないため、加算結果はわずかに0.3からずれます。厳密な===ではなく、誤差の範囲で比較する必要があります。
直し方: price === 0.3 を Math.abs(price - 0.3) < Number.EPSILON にします。
パターン2
1 const total = 1 - 0.9; 2 console.log(total === 0.1 ); ^
1 - 0.9も浮動小数点の誤差で0.1と厳密には一致しません。減算でも加算と同じ問題が起こります。
直し方: total === 0.1 を Math.abs(total - 0.1) < Number.EPSILON にします。
パターン3
1 const sum = 0.1 * 3; 2 console.log(sum === 0.3 ); ^
掛け算でも同じ誤差が生じます。0.1 * 3は0.30000000000000004になり、0.3とは===で一致しません。
直し方: sum === 0.3 を Math.abs(sum - 0.3) < Number.EPSILON にします。
パターン4
1 const value = 0.2 + 0.4; 2 console.log(value === 0.6 ); ^
0.2と0.4の加算でも同じ誤差が生じます。===での比較はほぼ確実に失敗します。
直し方: value === 0.6 を Math.abs(value - 0.6) < Number.EPSILON にします。
パターン5
1 const remainder = 0.3 - 0.2; 2 console.log(remainder === 0.1 ); ^
引き算でも同じ問題が起きます。誤差の範囲で比較するのが唯一の確実な方法です。
直し方: remainder === 0.1 を Math.abs(remainder - 0.1) < Number.EPSILON にします。
よくある誤解
「小数の計算は正確なはず」という前提はJavaScriptに限らずほとんどの言語の浮動小数点数に当てはまりません。厳密な一致ではなく、Number.EPSILONのような小さな許容誤差の範囲で比較する必要があります。
実務での勘所
Number.EPSILONを使った比較(Math.abs(a - b) < Number.EPSILON)は、1前後の小さな数同士を比べる場面ではよく機能しますが、絶対的な万能薬ではありません。非常に大きな数同士(1e20同士など)や非常に小さな数同士を比較する場合、その桁数において意味のある誤差の大きさがEPSILON(約2.22×10のマイナス16乗)とは桁違いになるため、比較先の数値の大きさに応じてスケールさせた許容誤差を使う必要があります。単純にEPSILONをコピペするだけでは不十分な場面があることを覚えておくと安全です。