PHPプログラムに関する各種メモ書き

900 * (1.1) =990 を小数点第一位で切り上げると答えは 991 になる

PHP でも Perl でもいいのですが以下のような計算を小数点第一位で切り上げると、我々人間が予想した数値と違う結果になります。

■ PHP

$a = 900 * (1.1);
print $a."\n";

$b   = ceil( $a );
print $b."\n";

■ Perl

use POSIX;
my $a = 900 * (1.1);
print $a."\n";

my $b   = ceil( $a );
print $b."\n";

いずれも答えは

990
991

となります。

PHPマニュアル:http://manual.xwd.jp/language.types.float.html

によると

0.1 や 0.7 のような簡単な小数表現も、若干精度を失うことなく内部的な 2 進表現に変換することはできません。 これにより、混乱する結果を生じることがあります。 つまり floor((0.1+0.7)*10) は、 予想される 8の代わりに実際の内部表現の結果として 7.9999999999... のようなものを結果として返します。

これは、有限の桁数の小数点表記で正確に表現できない分数が存在するという事実に関係しています。 例えば、1/3 の小数表記は 0.3333333. . . となります。

よって、小数の最後の桁を信用してはいけませんし、 小数が等しいという比較を行ってはいけません。より高い精度が必要な場合には、 任意精度数学関数または gmp 関数を代わりに使用してください。

との事。

● bc math を使用する

先ほどの計算は bc math 関数を使って 次のように書き換えることができます。

$a = bcmul( 900 , 1.1 , 99);
echo $a;
echo ceil($a);

● be math 関数

bcadd — 2つの任意精度の数値を加算する
bccomp — 2 つの任意精度数値を比較する
bcdiv — 2つの任意精度数値で除算を行う
bcmod — 2 つの任意精度数値の剰余を取得する
bcmul — 2つの任意精度数値の乗算を行う
bcpow — 任意精度数値をべき乗する
bcpowmod — 任意精度数値のべき乗の、指定した数値による剰余
bcscale — すべての BC 演算関数におけるデフォルトのスケールを設定/取得する
bcsqrt — 任意精度数値の平方根を取得する
bcsub — 任意精度数値の減算を行う
No.587
04/19 18:02

edit