PHP 於浮點數比對都是正常的, 但是如果其中一個浮點數有做過運算, 比對起來就都會是 False.
版本:
- PHP 5.2.5-3 with Suhosin-Patch 0.9.6.2 (cli)
- PHP 5.1.6 (cli)
- 目前測試於此二個版本, 都有此狀況.
詳可見下範例:
<?php
$a = 43.31;
$b = 7.51 + 35.80;/* 第一次ooxx */
echo ($a == $b) ? "ok\n" : "no\n"; // no/* 先強迫轉一次浮點數 */
$a = floatval($a);
$b = floatval($b);/* 再確認一次型態都相同 */
echo gettype($a) . "\n"; // double
echo gettype($b) . "\n"; // double
echo gettype(43.31) . "\n"; // double/* 第二次ooxx, $b != 43.31 */
echo ($a == 43.31) ? "a=43.31\n" : "a!=43.31\n"; // a=43.31
echo ($b == 43.31) ? "b=43.31\n" : "b!=43.31\n"; // b!=43.31/* 再確認一次型態, 值 都相同 */
var_dump($a); // float(43.31)
var_dump($b); // float(43.31)echo $a . "\n"; // 43.31
echo $b . "\n"; // 43.31/* 第三次ooxx, $a != $b => 43.31 != 43.31? */
echo ($a == $b) ? "ok\n" : "no\n"; // no/* 轉換成字串來比對 */
$a = strval($a);
$b = strval($b);
echo $a . "\n"; // 43.31
echo $b . "\n"; // 43.31
echo ($a == $b) ? "ok\n" : "no\n"; // ok/* 試試後面多 0 的狀況 */
$a = 43.31;
$b = 7.51 + 35.800;
// 保險點可以先轉 float 後, 再轉 str
// ex: $a = strval(floatval($a));
$a = strval($a);
$b = strval($b);
echo $a . "\n"; // 43.31
echo $b . "\n"; // 43.31
echo ($a == $b) ? "ok\n" : "no\n"; // ok
?>
上述例子遇到的狀況就是, $a = 43.31, $b = 7.51 + 35.80, 明明都是 43.31, 但是比對判斷 $a 就是都不等於 $b, 不知道是踩到地雷, 還是這樣子的寫法本來就不對, 還請有經驗的人指導. Orz.
最後想到的解法是, 轉成字串再來比對就通過了....
其它解法
感謝 Jace 的指點: 電腦就是差分器, 它的小數是用 1/2 + 1/4 + 1/8 .... + 1/n 這樣的做法, 所以有出現 000001 的差距(雖然我也是猜想這樣, 但是印不出 000001 就是覺得怪怪的), 0000001 這種是不會被表示出來的.
參考 小數點不準 這篇文章的內容, 看起來是用 bcadd 來解決, 詳如下:(範例有另外使用 bccomp 來做比對動作)
<?php
$a = 43.31;
$b = bcadd(7.51, 35.8, 2);
/* 下述兩者比對方式都可以 */
echo ($a == $b) ? "ok\n" : "no\n"; // ok
var_dump((0 == bccomp($a, $b, 2))); // true
?>
註: bcadd() 的方式是, 前面兩個參數相加, 第三個參數是要取幾位, 此範例是要取兩位, 所以寫 2, 超過小數 2位的會自動被移除, 所以值就會相等就不會有 000001 的狀況.