$res = 16244799483;
echo $res % 9999999; // 輸出 -5069794(預期 4801107)
同樣邏輯在 32 位系統或舊版 PHP 更易觸發。
% 運算子會將操作數轉回整數 運算,導致精度遺失並產生負數。floatval() 強制轉浮點後,% 仍會轉整數,故無效:$res = floatval(16244799483);
var_dump($res % 9999999); // 仍輸出 int(-5069794)
fmod(float $x, float $y) 專為浮點數取模設計,不轉整數。
$res = floatval(16244799483);
echo fmod($res, 9999999); // 輸出 4801107(正確)
function safe_mod($dividend, $divisor) {
return (int) fmod((float) $dividend, (float) $divisor);
}
// 使用
echo safe_mod(16244799483, 9999999); // 4801107
echo safe_mod('16244799483', '9999999'); // 字串也可
PHP 8.0 引入 intdiv()(整數除法)並改進大整數處理,但 % 行為未變。跨版本相容仍建議用 fmod()。
$n = 1000000;
$start = microtime(true);
for ($i=0; $i<$n; $i++) { $a = 12345678901234 % 9999; }
echo "mod: " . (microtime(true)-$start) . "s\n";
$start = microtime(true);
for ($i=0; $i<$n; $i++) { $a = fmod(12345678901234.0, 9999.0); }
echo "fmod: " . (microtime(true)-$start) . "s\n";
通常 fmod 慢 < 2 倍,可接受。
%。float,改用 fmod()。(int) 或 intval()。