小数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); ?>
上記ソースの実行結果
いやん。
対策としては上記リンクの通りに、 任意精度数学関数または gmp 関数を代わりとして使うのが正攻法。
でも、めんどくさいのでそこまでやらなくても、
小数をランダムで得るために
$num1 = mt_rand(1, 99) / 10 ;
10で割ってたわけだから、10で割る前に答を計算しといて、表示するときに最後に10で割るという乱暴な方法に書き換えて、事なきを得た。
小数とかそうそう扱わないから知らなかったし、バグ報告なかったら、エラーとか警告とか出ないから、地味に怖いな…。