php的%运算符仅支持整数,浮点数会报错或异常;fmod()可处理浮点但行为不同;大整数应使用bcmod()或gmp_mod()。

PHP 里 % 运算符只支持整数,浮点数直接报错
PHP 的取模运算符 % 看似简单,但一用就错——它**不接受浮点操作数**。哪怕你传的是 5.0 或 (int)3.9,只要底层类型是 float,运行时就会抛出 Warning: A non-integer value encountered(PHP 8.0+)或静默转为 int 导致结果异常(旧版本)。
常见踩坑场景:从表单、JSON 或数据库读出来的数字,表面是整数,实际是 string 或 float 类型;或者计算过程中用了 /,结果自动变成 float。
- 用前先断言:
is_int($a) && is_int($b),别信变量名 - 强制转整:
(int)$a % (int)$b可行,但要注意溢出和负数截断行为(见下条) - 更安全的替代:用
fmod()处理可能含小数的场景(但它返回的是 float 余数,不是整数)
fmod() 返回的是 float 余数,和 % 行为不等价
fmod($a, $b) 能处理 float,但它的数学定义是「满足 $a = $b * floor($a/$b) + fmod($a,$b) 的余数」,这跟整数取模的向零截断逻辑不同。尤其当操作数为负时,结果差异明显:
var_dump(7 % 3); // int(1) var_dump(fmod(7, 3)); // float(1) <p>var_dump(-7 % 3); // int(-1) ← 向零截断 var_dump(fmod(-7, 3)); // float(-1)</p><p>var_dump(-7 % -3); // int(-1) var_dump(fmod(-7, -3)); // float(-1)</p><p>var_dump(7 % -3); // int(1) var_dump(fmod(7, -3)); // float(1)
看起来一样?再试这个:
立即学习“PHP免费学习笔记(深入)”;
var_dump(-8 % 3); // int(-2) var_dump(fmod(-8, 3)); // float(1)
原因:% 是整数除法余数(truncated division),fmod() 是浮点除法余数(Euclidean 风格变体)。所以别把 fmod() 当作 % 的“升级版”来用。
- 要严格整数模运算:确保输入是 int,用
% - 要兼容 float 输入且需数学上连续的余数:用
fmod(),但得接受返回 float - 想统一负数行为(比如总要正余数):自己写
($a % $b + $b) % $b,但仅限 $b > 0 且 $a, $b 是 int
从字符串或 JSON 拿来的“整数”很可能不是 int
HTTP 请求参数、json_decode() 默认结果、MySQL fetch_assoc() 的数值字段——这些地方出来的“数字”,PHP 很可能存成 string 或 float。而 % 对 string 会尝试转换,对 float 则直接警告。
典型错误现象:Warning: A non-integer value encountered in ... on line X,但脚本继续执行,余数却是错的(比如 "10" % "3" 得到 1,看似对,但 "10.0" % "3" 就崩了)。
- 检查类型:
gettype($a)比is_numeric($a)更准 - 稳妥转换:
filter_var($a, FILTER_VALIDATE_INT),失败返回 false,比(int)强制转换更可控 - 数据库字段:PDO 中设
PDO::ATTR_EMULATE_PREPARES = false+PDO::ATTR_STRINGIFY_FETCHES = false,可减少自动 string 化
大整数超出 int 范围时,% 会静默溢出
PHP 的 int 有平台限制(32 位系统最大约 21 亿,64 位约 9E18)。如果两个 bigint 字符串(比如 UUID 时间戳、加密 ID)直接强转 (int) 再取模,高位直接丢弃,余数毫无意义。
例如:(int)"9223372036854775808" === 0(在 64 位 PHP 中已超 PHP_INT_MAX),接着 0 % 100 得 0,完全失真。
- 确认是否真需要整数模:如果是哈希分片、ID 路由,通常可用
bcmod()(bcmath 扩展)处理任意长度数字字符串 -
bcmod("9223372036854775808", "100")→"8",安全可靠 - 没开 bcmath?用 GMP:
gmp_strval(gmp_mod("9223372036854775808", "100")) - 注意:
bcmod()不支持负数被除数,需自行处理符号
真正麻烦的从来不是语法怎么写,而是你根本没意识到那个“整数”早就不是整数了,或者它大到连 int 都装不下。











