<?php $f = 0.58; var_dump(intval($f * 100)); // 輸出 57,而非預期 58
許多開發者以為是 PHP Bug,實則是 **IEEE 754 雙精度浮點數表示限制** 導致。
0.58 和 0.57 在二進制中均為**無限循環小數**,只能截斷 52 位尾數:
0.58 ≈ 0.0010100011110101110000101000111101011100001010001111 (二進制)
→ 實際儲存值 ≈ 0.57999999999999996
0.57 ≈ 0.0010001111010111000010100011110101110000101000111101
→ 實際儲存值 ≈ 0.56999999999999995
0.58 * 100 在浮點運算中結果約為 57.999999999...,intval() 直接截斷小數部分 → 57。
「你看似有限的十進制小數,在計算機二進制中卻是無限循環的。」
這**不是 PHP Bug**,是所有遵循 IEEE 754 的語言(C、Java、JavaScript、Python...)的共同特性。
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,因此:
PHP_INT_MAX = 9223372036854775807 (2^63-1),但**精確轉換上限為 2^53-1 = 9007199254740991**。超過此值的整數轉 float 再轉回會丟失精度。PHP_INT_MAX = 2147483647,在 53 bits 範圍內,轉換安全。<?php var_dump(9007199254740991 === (int)(float)9007199254740991); // true var_dump(9007199254740992 === (int)(float)9007199254740992); // false! 精度丟失
建議:大整數 (ID、金額分) **統一用字串存儲、BCMath 運算**,避免隱式轉換陷阱。