首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 浮點數經典陷阱:為什麼 intval(0.58 * 100) = 57?

     編輯:PHP 相關知識

現象

<?php
$f = 0.58;
var_dump(intval($f * 100)); // 輸出 57,而非預期 58

許多開發者以為是 PHP Bug,實則是 **IEEE 754 雙精度浮點數表示限制** 導致。

底層原理深度解析

1. 雙精度結構 (64 bits)

  • 1 bit 符號位 (S)
  • 11 bits 指數位 (E),偏移碼
  • 52 bits 尾數位 (M),含隱含首位 1 共 53 bits 有效精度

2. 十進制小數轉二進制

0.58 和 0.57 在二進制中均為**無限循環小數**,只能截斷 52 位尾數:

0.58 ≈ 0.0010100011110101110000101000111101011100001010001111 (二進制)
     → 實際儲存值 ≈ 0.57999999999999996
0.57 ≈ 0.0010001111010111000010100011110101110000101000111101
     → 實際儲存值 ≈ 0.56999999999999995

3. 乘法與截斷

0.58 * 100 在浮點運算中結果約為 57.999999999...,intval() 直接截斷小數部分 → 57。

關鍵結論

「你看似有限的十進制小數,在計算機二進制中卻是無限循環的。」

這**不是 PHP Bug**,是所有遵循 IEEE 754 的語言(C、Java、JavaScript、Python...)的共同特性。

正確處理姿勢

方案一:BCMath 高精度 (推薦金融/關鍵業務)

echo intval(bcmul('0.58', '100', 2)); // 58 ✓

方案四捨五入後再取整

echo intval(round(0.58 * 100, 2)); // 58 ✓
// 或
echo (int)round(0.58 * 100); // 58

方案三:字串格式化

echo (int)sprintf('%.2f', 0.58 * 100); // 58

方案四:整數分/厘存儲 (架構層面徹底解決)

資料庫、緩存、程式內部**統一以「分」為單位存整數**:

$priceCents = 5800; // 58.00 元
$total = $priceCents * $quantity; // 純整數運算,零誤差
echo $total / 100 . ' 元'; // 仅輸出時格式化

延伸:整數與浮點數轉換的安全邊界

PHP 整數長度隨平台 (32/64 位)。雙精度尾數 53 bits,因此:

  • **64 位系統**:PHP_INT_MAX = 9223372036854775807 (2^63-1),但**精確轉換上限為 2^53-1 = 9007199254740991**。超過此值的整數轉 float 再轉回會丟失精度。
  • **32 位系統**:PHP_INT_MAX = 2147483647,在 53 bits 範圍內,轉換安全。
<?php
var_dump(9007199254740991 === (int)(float)9007199254740991); // true
var_dump(9007199254740992 === (int)(float)9007199254740992); // false! 精度丟失

建議:大整數 (ID、金額分) **統一用字串存儲、BCMath 運算**,避免隱式轉換陷阱。

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