首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

PHP floor() 浮點數精度陷阱:271.28 * 100 = 27127 根因與解決方案

     編輯:程式設計綜合問答

現象重現

$money = 271.29;
echo floor($money * pow(10, 2)); // 輸出 27129 ✓

$money = 271.28;
echo floor($money * pow(10, 2)); // 輸出 27127 ✗ (預期 27128)

根本原因:IEEE 754 雙精度浮點數表示誤差

271.28 無法在二進位浮點數中精確表示,實際儲存值略小於 271.28:

271.28 * 100 = 27127.999999999996... (非 27128.0)
floor(27127.999999999996) = 27127

驗證程式碼

printf("%.20f\n", 271.28 * 100); 
// 輸出:27127.9999999999963620

為何 271.29 正常而 271.28 異常?

數值二進位表示乘 100 結果floor 結果
271.29略大於真值27129.00000000000427129 ✓
271.28略小於真值27127.99999999999627127 ✗

浮點數誤差方向取決於具體數值的二進位展開特性,非系統性偏差。

解決方案對比

方案一:四捨五入後再 floor(推薦)

echo floor(round($money * 100, 0)); // 27128 ✓
// 或
echo (int)round($money * 100); // 27128 ✓

原理:round() 先處理浮點誤差得到最接近整數,再轉型。

方案二:BCMath 高精度數學擴展(金融級)

// 需啟用 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()風險最低、相容性佳

PHP 官方文件警示

⚠️ PHP 手冊明確警告

浮點數精度問題:簡單的十進位分數如 0.1 或 0.7 無法在內部二進位格式中精確表示... 永遠不要信任浮點數結果精確到最後一位。

參考:PHP: Floating point numbers

結語

PHP floor()「錯誤」實為浮點數本質特性。金融計算請務必使用 BCMath 或整數分單位;一般場景以 round() 修正即可。理解 IEEE 754 浮點數表示限制,是每位後端開發者的必修課。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved