經典面試題:var_dump((0.1 + 0.7) == 0.8); 輸出 bool(false)。這不是 Bug,而是 IEEE 754 雙精度浮點數標準 導致的必然結果——十進位小數無法精確用二進位有限位數表示(如 0.1 在二進位是無限循環小數)。
PHP 手冊明確警告:永遠不要相信浮點數精確到最後一位,也永遠不要直接比較兩個浮點數是否相等。
<?php $a = 0.1; $b = 0.7; var_dump($a + $b); // float(0.8) 顯示正常 var_dump(($a + $b) == 0.8); // bool(false) 實際為 0.7999999999999999 var_dump(floor((0.1+0.7)*10)); // int(7) 而非預期 8 ?>
PHP 內建 BCMath 擴展(通常預設開啟)提供字串級高精度運算,避開浮點誤差。
| 運算 | 浮點數寫法 | BCMath 寫法 | 說明 |
|---|---|---|---|
| 加法 | $a + $b | bcadd($a, $b, $scale) | $scale 為小數位數 |
| 減法 | $a - $b | bcsub($a, $b, $scale) | |
| 乘法 | $a * $b | bcmul($a, $b, $scale) | |
| 除法 | $a / $b | bcdiv($a, $b, $scale) | 需指定 $scale,否則截斷 |
| 取模 | $a % $b | bcmod($a, $b) | |
| 冪次 | $a ** $b | bcpow($a, $b, $scale) | |
| 比較 | $a == $b | bccomp($a, $b, $scale) | 返回 -1/0/1 |
<?php $a = '0.1'; $b = '0.7'; // BCMath 接受字串參數 $sum = bcadd($a, $b, 2); // '0.80' var_dump($sum == '0.8'); // bool(true) ✅ // 金額計算最佳實踐:統一用分(整數)或 BCMath $price1 = '19.99'; $price2 = '0.01'; $total = bcadd($price1, $price2, 2); // '20.00' ?>
<?php
bcscale(2); // 後續 bcadd 等可省略 $scale 參數
echo bcadd('0.1', '0.7'); // 0.80
?>
對於大整數運算(如加密、階乘),gmp_* 函數效能更優:
<?php
$n = gmp_init('12345678901234567890');
echo gmp_strval(gmp_mul($n, $n)); // 大整數乘法
?>
abs($a-$b) < 1e-9 容差判斷。DECIMAL(19,4),勿用 FLOAT/DOUBLE。掌握精度陷阱與工具選型,是 PHP 後端工程師的基本功。