2026.09.23 泊まれる場所4,223 立ち寄りスポット59,693 営業中を確認したキャンプ場1,005 note を読む

0.1+0.2が0.3にならない理由|PHPではfloor((0.1+0.7)*10)が8ではなく7になる

JavaScriptで0.1+0.2を計算すると0.30000000000000004になる。
原因は、小数を二進数で持つ仕組みにある。
比較・お金・表示の三つの場面で、どう書けばいいかをまとめた。

JavaScript で 0.1 + 0.2 を計算すると、0.3 ではなく 0.30000000000000004 になる。
PHP のマニュアルには、floor((0.1+0.7)*10) が 8 ではなく 7 になる例まで載っている。
原因はバグではなく、小数を二進数で持つ仕組みそのものにある。
この記事では、なぜそうなるのかと、比較・お金・表示の三つの場面でどう書けばいいかをまとめる。

最初にこれを見ると、コンピューターは計算が苦手なのかと思ってしまう。
でも計算を間違えているわけではない。
0.1 という数を、最初からぴったりには持てていないだけだ。

0.1 は、二進数では割り切れない

十進数で 1 ÷ 3 を書こうとすると、0.3333… と終わらなくなる。
どこかで切るしかないので、少しだけ誤差が残る。

二進数でも同じことが起きる。
二進数で割り切れるのは、2 で何回か割った形の数だけだ。
0.5 や 0.25 はぴったり書けるが、0.1 や 0.7 は二進数にすると終わらない小数になる。

PHP のマニュアルにも、0.1 や 0.7 のような数は二進数の浮動小数点数としては正確に表現できない、と書いてある。
仮数部をいくら大きくしても同じだ、とも書いてある。
桁を増やせば解決する話ではない、ということだ。

PHP も JavaScript も、同じ「倍精度」で持っている

PHP のマニュアルによると、PHP は通常 IEEE 754 倍精度フォーマットを使う。
丸めの誤差は 1.11e-16 のオーダーで、計算を重ねると誤差はもっと大きくなる。

JavaScript の Number も同じ倍精度の形式だ。
だから二つの言語で同じ現象が出る。
言語の癖ではなく、ほとんどの言語に共通する話だと思っておいたほうがいい。

// JavaScript
console.log(0.1 + 0.2);         // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false

PHP の例は、中の値が 7.9999999999999991118… のようになっているから 7 になる。
floor は切り捨てなので、8 にほんの少し届かない値が 7 に落ちる。
切り捨てや比較のように「境目」で判定する処理ほど、この誤差が表に出やすい。

小数どうしを == で比べない

PHP のマニュアルは、小数を直接比較して等しいかどうかを調べてはいけない、と書いている。
代わりに、差がじゅうぶん小さければ同じとみなす。

JavaScript には Number.EPSILON という定数がある。
1 と、その次に表せる数との差で、およそ 2.22e-16 だ。

ただ、MDN はこの値をそのまま閾値に使わないように、と注意している。
1000.1 + 1000.2 のように数が大きくなると、誤差は 10 の -13 乗くらいまで膨らむ。
Number.EPSILON で比べると、同じはずの値が「違う」と判定されてしまう。
閾値は、扱う数の大きさと、何桁まで合っていればいいかで決める。

閾値を書く前に「この数字は何桁まで意味があるのか」を先に決めるのがいい、と僕は思っている。
温度なら小数第1位、距離なら小数第2位、というふうに、意味のある桁が決まれば閾値も自然に決まる。

お金は、そもそも小数で持たない

金額の計算に小数を使うと、この誤差が一円単位のずれとして出てくることがある。
僕の考えでは、いちばん確実なのは小数を使わないことだ。

円なら整数のまま扱う。
ドルのように小数点以下がある通貨なら、セント単位の整数にしてから計算する。
税や割引で端数が出るところだけ、どこで、どの向きに丸めるかを決めて一回だけ丸める。

どうしても小数のまま正確に計算したいなら、PHP のマニュアルは任意精度数学関数か gmp 関数を使うように案内している。
どちらも拡張機能なので、使う前にサーバーで有効になっているかを確かめておきたい。

表示のときは、桁を決めて丸める

計算結果を画面に出すだけなら、表示する桁数を決めて丸めればいい。
PHP なら round や number_format、JavaScript なら toFixed が使える。

ここで気をつけたいのは、丸めるのは「最後に一回」ということだ。
途中で何度も丸めると、丸めの誤差が積み重なって、かえって合計が合わなくなる。
計算は丸めずに進めて、人に見せる直前で丸める。
この順番を守るだけで、見た目のおかしな数字はかなり減る。

型の比較で似た落とし穴を踏んだ話は、PHPの「==」の罠に書いた。
数字の意味をコードに残す話は、マジックナンバーを定数に置き換えるにまとめている。

まとめ

0.1 + 0.2 が 0.3 にならないのは、0.1 を二進数でぴったり持てないからだ。
小数は == で比べず、差が小さいかで比べる。
お金は整数で持ち、表示の直前で一回だけ丸める。
この三つを押さえておけば、「計算が合わない」で半日つぶれることはだいぶ減ると思う。

出典

まる子パパ

まる子パパ

会社員。受託開発のエンジニアを15年以上続けていて、その前は寿司職人・長距離運転手・農業。 技術ブログは、キャンプの合間に踏んだバグと、AI・Web開発の運用の失敗を書いています。 このサイト自体が、開発に携わっているCMS「コンテナ」の稼働中の実例です。 このサイトについて / 小さな自動化の相談

—

ほかにも書いています

受託・note・スタンプ

※受託の相談とnoteは、運営者が個人で受けている・書いているものです。

技術ブログ一覧へ戻る