本文從 PHP 內核視角(以 PHP 5.2 為例)剖析浮點數在 zval 結構中的存儲方式、長整型與雙精度浮點數的轉換邊界、以及 32/64 位平台差異導致的精度陷阱,並給出安全處理大整數的建議。
struct _zval_struct {
zvalue_value value; // 實際數值
zend_uint refcount;
zend_uchar type; // IS_LONG, IS_DOUBLE 等
zend_uchar is_ref;
};
typedef union _zvalue_value {
long lval; // 長整型(32/64 位隨平台)
double dval; // 雙精度浮點數(固定 64 位,IEEE 754)
struct { char *val; int len; } str;
HashTable *ht;
zend_object_value obj;
} zvalue_value;
關鍵點:long 長度隨平台(32 位或 64 位),而 double 固定 64 位,尾數 52 位 + 隱含 1 位 = 53 位有效精度。
問題:long a = x; assert(a == (long)(double)a); 在什麼範圍內成立?
答案:|x| ≤ 253 - 1 = 9,007,199,254,740,991。超過此值,整數無法在 double 尾數中精確表示,轉換會丟失精度。
$a = 9223372036854775807; // 64 位有符號最大值
$b = 9223372036854775808; // 超過最大值
var_dump($a); // int(9223372036854775807)
var_dump($b); // float(9.22337203685E+18)
超出 PHP_INT_MAX 的字面量直接作為 float 存儲,精度已損失。
$a = 9223372036854775807;
$b = 9223372036854775808;
var_dump($a === ($b - 1)); // 64 位 true,32 位 false(精度丟失)
同樣代碼在不同位數平台行為不一致,極易埋下隱患。
PHP_INT_MAX 判斷平台整數上限。php.ini 中 precision 只影響輸出顯示,不影響內部精度。理解 zval 結構、IEEE 754 尾數限制、平台長整型差異,是編寫高可靠 PHP 數值代碼的基石。對於超出 53 位精度的整數,請堅持「字符串存儲、庫函數計算」原則。