$money = 271.29;
echo floor($money * pow(10, 2)); // 輸出 27129 ✓
$money = 271.28;
echo floor($money * pow(10, 2)); // 輸出 27127 ✗ (預期 27128)
271.28 無法在二進位浮點數中精確表示,實際儲存值略小於 271.28:
271.28 * 100 = 27127.999999999996... (非 27128.0)
floor(27127.999999999996) = 27127
printf("%.20f\n", 271.28 * 100);
// 輸出:27127.9999999999963620
| 數值 | 二進位表示 | 乘 100 結果 | floor 結果 |
|---|---|---|---|
| 271.29 | 略大於真值 | 27129.000000000004 | 27129 ✓ |
| 271.28 | 略小於真值 | 27127.999999999996 | 27127 ✗ |
浮點數誤差方向取決於具體數值的二進位展開特性,非系統性偏差。
echo floor(round($money * 100, 0)); // 27128 ✓
// 或
echo (int)round($money * 100); // 27128 ✓
原理:round() 先處理浮點誤差得到最接近整數,再轉型。
// 需啟用 php_bcmath.dll
echo bcmul($money, '100', 0); // "27128" ✓
// 或配合 bcdiv
echo bcdiv(bcmul($money, '100'), '1', 0); // "27128"
優點:任意精度、無浮點誤差、金融計算標準
echo floor(strval($money * pow(10, 2))); // 27128 ✓
缺點:依賴 PHP 字串轉換行為、不可移植、語意不清、效能較差
// 內部以「分」為單位儲存整數
$moneyCents = 27128; // int
// 顯示時除以 100 格式化
echo number_format($moneyCents / 100, 2, '.', ''); // "271.28"
優點:從根本消除浮點誤差、效能最佳、會計系統標準作法
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 簡單顯示、非關鍵計算 | round() + floor() | 零依賴、一行修正 |
| 金融/會計/電商金額計算 | BCMath 或 整數分單位 | 精度保證、合規要求 |
| 高效能、大量計算 | 整數分單位 | 無浮點運算開銷 |
| 舊系統快速修補 | round() | 風險最低、相容性佳 |
浮點數精度問題:簡單的十進位分數如 0.1 或 0.7 無法在內部二進位格式中精確表示... 永遠不要信任浮點數結果精確到最後一位。
PHP floor()「錯誤」實為浮點數本質特性。金融計算請務必使用 BCMath 或整數分單位;一般場景以 round() 修正即可。理解 IEEE 754 浮點數表示限制,是每位後端開發者的必修課。