PHPで浮動小数点数を比較する際の落とし穴と正しい比較方法
PHPでは、浮動小数点数が等しいかどうかを「==」演算子で直接比較すると、直感に反する結果になることがあります。見た目上は同じ値に見える浮動小数点数でも、コンピュータ内部での表現方法の違いにより、実際には完全に同一の値ではない場合があるためです。
本記事では、浮動小数点数の比較で発生する問題を具体例とともに解説し、この問題を回避するための実践的な手法を紹介します。
問題の例
まず、シンプルな例でこの問題を確認してみましょう。
<?php
$a = 0.14;
$b = 1 - 0.86; // 0.14
if($a == $b ){
echo 'a and b are same';
}
else {
echo 'a and b are not same';
}
?>出力結果:
a and b are not same.
解説:
$a と $b はどちらも 0.14 を表しているはずなのに、else 側の処理が実行されました。これは、浮動小数点数がコンピュータ内部では2進数(IEEE 754形式)で表現されるため、「1 - 0.86」という計算結果は厳密には 0.14 にならず、ごくわずかな誤差(丸め誤差)が生じるからです。
それでは、実際の開発でこの問題を回避するための具体的な方法を、ケース別に見ていきましょう。
ケース1:許容誤差(イプシロン)を使った比較
1つ目の方法は、2つの値の差が十分に小さいかどうかで判定する方法です。コンピュータシステムで扱える浮動小数点数の最小の差(イプシロン)を基準に比較を行います。
例:
<?php
$val1 = 2.183459;
$val2 = 2.183450;
$epsilon = 0.00001;
if(abs($val1 - $val2) < $epsilon) {
echo "True";
}
else {
echo "False";
}
?>出力結果:
True
解説:
ここでは、2つの浮動小数点変数 $val1 と $val2 を用意し、PHP組み込み関数の abs() を使って両者の差の絶対値を求めています。その絶対値が、あらかじめ定義した許容誤差 $epsilon(0.00001)より小さければ「等しい」とみなす仕組みです。
なぜ絶対値を使うのかというと、$val1 と $val2 のどちらが大きいかに関わらず、差の大きさだけを評価できるようにするためです。この例では、両者の値は小数第5位まで同じ桁を持っており、直接比較ではPHPが正確に判定できないほど微妙な差しかありません。そのため、イプシロンによる比較が有効になります。
ケース2:round() 関数を使った比較
2つ目の方法は、round() 関数を使って値を指定した桁数で四捨五入してから比較する方法です。
例:
<?php $val1 = 9 - 6.2; $val2 = 1.8; var_dump(round($val1, 2) == round($val2, 2)); ?>
出力結果:
bool(true)
解説:
ここでも2つの浮動小数点変数 $val1 と $val2 を用意しています。「9 - 6.2」は数学的には 1.8 ですが、内部的にはわずかな誤差を含んでいます。そこで、組み込み関数 round() を使い、両方の値を小数第2位で四捨五入してから比較することで、期待通りの結果を正しく得ることができます。
このように、浮動小数点数を比較する際は、「==」演算子による直接比較を避け、イプシロンによる許容誤差の判定や round() による丸め処理を活用することが重要です。金額計算などさらに高い精度が求められる場面では、BCMath拡張モジュールの bccomp() 関数の利用も検討するとよいでしょう。
-
C#の浮動小数点リテラルとは?基本構造と使い方をわかりやすく解説
浮動小数点リテラルの基本構造浮動小数点リテラルは、「整数部」「小数点」「小数部」「指数部」という要素で構成されます。C#では、このリテラルを10進形式または指数形式のどちらかで表現することが可能です。浮動小数点リテラルの記述例9.23456 269485E-5F上記のように、通常の小数表記に加え、「E」を使った指数表記も利用できます。また、数値の末尾に「F」(または小文字の「f」)を付けることで、そのリテラルがfloat型であることを明示的に指定できます。サフィックスを省略した場合、実数リテラルは既定でdouble型として扱われるため注意が必要です。浮動小数点リテラルを出力するサンプルコードそ
-
【C#入門】整数リテラルと浮動小数点リテラルの使い方を徹底解説
整数リテラルとはC#における整数リテラルは、10進数または16進数の定数として記述できます。基数(radix)は接頭辞(プレフィックス)で指定し、16進数の場合は「0x」または「0X」を値の先頭に付加します。10進数の場合には接頭辞を付けません。さらに、値の末尾にサフィックスを付けることで型を明示することも可能です。例えば「u」または「U」を付けるとunsigned int型になります。以下に整数リテラルの例を示します。10 // int 18u // unsigned intそれでは、これらのリテラルを使って実際に変数を宣言・初期化してみましょう。// int int a = 10