PHPの数値について考える(3)
浮動小数点数のページを見た後、整数の項目で気になるのがありました。
echo (int) ( (0.1+0.7) * 10 )."\n"; // 7が出力されます!
これって本当!?
って、ことで試してみました。
本当に「7」だ。
説明は浮動小数点数の精度で書いてあります。
さらに、十進数では正確な小数で表せる有理数、たとえば 0.1 や 0.7 は、 二進数の浮動小数点数としては正確に表現できません。 これは、仮数部をいくら大きくしても同じです。 したがって、それを内部的な二進数表現に変換する際には、どうしても多少精度が落ちてしまいます。 その結果、不思議な結果を引き起こすことがあります。たとえば、 floor( (0.1 + 0.7) * 10) の結果はたいてい 7 となるでしょう。おそらくは 8 を想定していらっしゃるでしょうが、そのようにはなりません。 これは、(この計算結果の) 内部的な値が 7.9999999999999991118... のようになっているからです。
なるほど。納得です。
というわけで、すこし実験。
(int) ( (0.1+0.7) * 10 )
echo ( (0.1+0.7) * 10 );
echo ( (0.1 * 10) + (0.7* 10 ));
(int)( (0.1 * 10) + (0.7* 10 ))
$a = "0.1";
$b = "0.7";
echo (($a+$b) * 10 );
echo (int) (($a+$b) * 10 );
$c = ((0.1+0.7) * 10 );
echo $c;
echo (int)$c;
echo (int)floor($c);
echo (int)ceil($c);
結果
7 // サンプル
8 // キャストなし
8 // わけて計算8 // キャストあり。分けて計算
8 // 変数でやってみた
7 // キャストあり。変数でやってみた
8 // 計算結果を変数に代入
7 // キャストあり。計算結果を変数に代入
7 // 少数点以下を切り捨て
8 // 少数点以下を切り上げ
計算結果を代入した後のキャストありとなしはすごい気になった。
確かに計算結果を保持していると納得。
計算結果的に切り上げの判断が下されているところがすごく怖い。
var_dump($c);
結果
float(8)
PHPで計算するのが怖くなってしまった。。。