はじめに
PHP8の上級試験の範囲を勉強している中で浮動小数点同士の比較演算について考えることがあり、その時の内容をまとめましたので、共有したいと思います。
【PHP入門】なぜ 0.1 + 0.2 === 0.3 「等しくない」と判定されるのか?
PHPで次のようなコードを実行すると…
<?php
declare(strict_types=1);
error_reporting(-1);
if (0.3 === (0.1 + 0.2)) {
echo "等しい";
} else {
echo "等しくない";
}
出力結果は 等しくない
になります。
「え? 0.1 + 0.2
は 0.3
でしょ? なんで?」と思った方、安心してください。
これはPHPだけでなく、ほとんどのプログラミング言語で共通する落とし穴です。
浮動小数点数は “誤差” を含む
この現象の原因は、浮動小数点数(float)の誤差にあります。
PHPでは float
型の数値は、IEEE 754 という仕様に基づいた2進数の近似値として内部的に扱われます。このとき、十進数で表現できる「0.1」や「0.2」といった値は、実は2進数で正確に表現できないのです。
例(概念的に)
十進数 | 二進数での内部表現(概念) |
---|---|
0.1 | 0.0001100110011…(無限に続く) |
0.2 | 0.001100110011…(無限) |
0.3 | 0.0100110011…(無限) |
このように、ほんのわずかなズレ(誤差)が発生します。
実際に var_dump してみよう
var_dump(0.1 + 0.2); // float(0.30000000000000004)
var_dump(0.3); // float(0.3)
この通り、0.1 + 0.2
の結果は 0.30000000000000004 となっており、
PHPが内部的に保持している 0.3
とは完全に一致していないことがわかります。
したがって、===
(値も型も厳密に比較する演算子)では false
となり、
0.3 === (0.1 + 0.2) // false
という判定になるわけです。
浮動小数点数を比較するときの正しい方法
このような浮動小数点数の誤差に対処するためには、誤差を許容する比較方法を使うのが一般的です。
define('EPSILON', 0.00001);
if (abs((0.1 + 0.2) - 0.3) < EPSILON) {
echo "ほぼ等しい";
} else {
echo "等しくない";
}
このように「差が十分に小さいかどうか」で比較することで、意図通りの判定が可能になります。
おわりに:=== を使うときは float に注意!
===
を使った比較は非常に厳密です。特に float
型に対しては、一見等しいように見える値でも false になる可能性があるため注意しましょう。
この挙動を知らずにバグにハマるケースはとても多いので、これを機にしっかりと覚えておくと◎です!