【PHP】よくある勘違い、小数を足して比較してはダメ!解決方法も解説

PHP
Eslo
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.20.3に等しいはずなのに、なぜ一致しないのでしょうか?

コンピューターで小数が正確に扱えない理由

この問題の原因は、浮動小数点数が2進数で近似的に表現されているためです。以下のような流れで誤差が生じます。

1.10進数の小数を2進数に変換

  • 10進数の「0.1」を2進数で表現すると、0.000110011001100110011...と無限に続く値になります。
  • コンピューターはこれを有限の桁数に丸めて扱います。

2.丸めによる誤差が発生

  • 0.10.10000000000000000555のように近似値として格納されます。
  • 同様に0.20.20000000000000001110.30.3000000000000000444となります。

3.計算結果が正確に一致しない

  • 0.1 + 0.20.3000000000000000444となり、0.3と厳密に一致しません。

これが原因で比較ができないのです。

PHP:浮動小数点数-Manual

解決方法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を使用することで、精度を指定しつつ小数計算を行うことが可能です。

BCMath-Manual

まとめ

PHPで浮動小数点数を扱う際には、丸め誤差が発生することを理解することが重要です。直接比較は避け、以下の方法を使うことで安全に比較できます。

  • abs関数を使って誤差範囲内かを確認する
  • BC Mathを使用して正確に計算する

正しい理解で予期せぬバグを防ぎましょう!