【PHP】浮動小数点数の計算をさせると、結果が意味不明になる(ことがある)

(2)の答はもちろん0
(2)の答はもちろん0

小数3項の計算問題がリロードごとに自動生成されてpdfを吐き出す、というシステムですが、
(2)に注目。もちろん答は0です。(↑画像は修正後のシステムから生成したもの)

解答を算出する際に、

$ans = 5.2+1.9-7.1 ;

と計算させると、0にはならず、float 型のでたらめ(ではないんだけど)な数値になってしまいます。

調べてみるとどうやらphp浮動小数点数の扱いの仕様らしく…
浮動小数点数 – PHPプロ!マニュアル

0.1 や 0.7 のようなシンプルな小数であっても、 それを内部的な二進数表現に変換する際には、どうしても多少精度が落ちてしまいます。 その結果、不思議な結果を引き起こすことがあります。たとえば、 floor((0.1+0.7)*10) の結果はたいてい 7 となるでしょう。おそらくは 8 を想定していらっしゃるでしょうが、そのようにはなりません。 これは、(この計算結果の) 内部的な値が 7.9999999999… のようになっているからです。

なるほど。有名な仕様というわけだった。知らなかった。反省。

実験。

< ?php

$answer = 2.5 - 2.5 ; //もちろん0になってほしい
echo(' 0 になるはず ↓ <br />');
echo($answer);

echo('<hr />');

$answer = 2.5 - 2.4  ; //もちろん 0.1 になってほしい
echo(' 0.1 になるはず ↓ <br />');
echo($answer);


echo('<hr />');

$answer = 2.5 - 2.4 - 0.1 ; //もちろん0になってほしい
echo(' 0 になるはず ↓ <br />');
echo($answer);

?>

上記ソースの実行結果
0ninare1
いやん。

対策としては上記リンクの通りに、 任意精度数学関数または gmp 関数を代わりとして使うのが正攻法。
でも、めんどくさいのでそこまでやらなくても、
小数をランダムで得るために

$num1 = mt_rand(1, 99)  / 10 ;

10で割ってたわけだから、10で割る前に答を計算しといて、表示するときに最後に10で割るという乱暴な方法に書き換えて、事なきを得た。

小数とかそうそう扱わないから知らなかったし、バグ報告なかったら、エラーとか警告とか出ないから、地味に怖いな…。

コメントを残す

メールアドレスが公開されることはありません。