
Eslo
PHPで小数を扱う際に、「0.1 + 0.2 が 0.3 にならない」という現象に遭遇したことはありませんか?本記事では、この問題の原因を詳しく解説し、安全に小数を比較する方法を紹介します!
小数を足して比較するときに起きる問題とは?
PHPでは、小数(浮動小数点数)を扱う際に、意図しない結果になることがあります。以下のコードを見てみましょう。
$float_1 = 0.1 + 0.2;
$float_2 = 0.3;
var_dump($float_1 === $float_2 );
// 出力結果:false
このコードを実行すると、falseが出力されます。一見、0.1 + 0.2
は0.3
に等しいはずなのに、なぜ一致しないのでしょうか?
コンピューターで小数が正確に扱えない理由
この問題の原因は、浮動小数点数が2進数で近似的に表現されているためです。以下のような流れで誤差が生じます。
1.10進数の小数を2進数に変換
- 10進数の「0.1」を2進数で表現すると、
0.000110011001100110011...
と無限に続く値になります。 - コンピューターはこれを有限の桁数に丸めて扱います。
2.丸めによる誤差が発生
0.1
は0.10000000000000000555
のように近似値として格納されます。- 同様に
0.2
は0.2000000000000000111
、0.3
は0.3000000000000000444
となります。
3.計算結果が正確に一致しない
0.1 + 0.2
は0.3000000000000000444
となり、0.3
と厳密に一致しません。
これが原因で比較ができないのです。
解決方法1 (拡張モジュールなし)
浮動小数点数を正確に比較するためには、直接比較するのではなく、誤差の許容範囲を設定する方法が一般的です。
方法1: abs
関数を使った誤差の許容範囲の比較
PHPでは、abs
関数を使って2つの数値の差が非常に小さいかどうかを確認することで比較できます。
$float_1 = 0.1 + 0.2;
$float_2 = 0.3;
$epsilon = 0.00001; // 許容する誤差
if (abs($float_1 - $float_2 ) < $epsilon) {
echo "等しい!";
} else {
echo "等しくない!";
}
// 出力結果:等しい!
解決方法2 (拡張モジュールあり)
もし小数の計算を正確に行いたい場合は、BC Math拡張モジュールを使用するのがおすすめです。BC Mathは、任意精度の計算を可能にします。
以下に例を示します。
$float_1 = 0.1;
$float_2 = 0.2;
$float_3 = 0.3;
$sum = bcadd((string)$float_1, (string)$float_2, 1); // 小数第1位まで計算
if ($sum === (string)$float_3) {
echo '等しい!';
} else {
echo '等しくない!';
}
// 出力結果:等しい!
BC Mathを使用することで、精度を指定しつつ小数計算を行うことが可能です。
まとめ
PHPで浮動小数点数を扱う際には、丸め誤差が発生することを理解することが重要です。直接比較は避け、以下の方法を使うことで安全に比較できます。
abs
関数を使って誤差範囲内かを確認する- BC Mathを使用して正確に計算する
正しい理解で予期せぬバグを防ぎましょう!