gmp_fact()最可靠,因返回GMP对象无精度丢失;手写阶乘在21!(int)和17!(float)起分别溢出与失真;校验须用gmp_strval转字符串严格比较(===),并单独测试n=0。

用 gmp_fact() 直接比对最可靠
PHP 自带的 gmp_fact() 是唯一能准确计算大整数阶乘的函数,它返回 GMP 对象,不会溢出或丢失精度。如果你自己写了递归或循环版阶乘,校验时别转成 int 或 float 再比——一过 17! 就开始出错。
实操建议:
- 输入值
$n≤ 100 时,直接用gmp_strval(gmp_fact($n))得到精确字符串结果,和你的函数输出字符串比(===) - 别用
==或类型松散比较,0123和123字符串会误判 - 注意
gmp_fact(0)返回"1",不是空或 0,边界值必须单独测
手写阶乘函数常见溢出点在哪
用 int 累乘时,PHP 在 64 位系统上最大安全整数是 9223372036854775807(即 PHP_INT_MAX),而 21! ≈ 5.109e19,已经远超该值。从 21! 开始,int 版本就会静默溢出变负数或回绕,但不报错。
典型错误现象:
立即学习“PHP免费学习笔记(深入)”;
-
factorial(20)还对,factorial(21)突然变成负数或极小正数 - 用
float实现,17!起就开始丢精度(float只有约 15–17 位有效数字) - 递归没设
if ($n ,factorial(-1)无限递归或报错
不用 GMP 怎么做轻量校验
如果环境禁用 GMP 扩展,又不想引入 bcmath,可以用质因数分解法做快速合理性检查:阶乘结果中质数 2 的幂次一定 ≥ 质数 5 的幂次,且末尾零的个数等于 floor(n/5) + floor(n/25) + floor(n/125) + ...。
实操建议:
- 算出你函数输出的字符串长度、末尾零个数,和理论值比(例如
100!应有24个末尾零) - 用
bcadd()和bcmul()重写一个 bcmath 版本,交叉验证n ≤ 1000以内结果 - 对
n ≤ 10的情况,硬编码一组已知正确值(如[1,1,2,6,24,120,...])做回归测试
测试时容易漏掉的边界和陷阱
阶乘校验不是只看“大数对不对”,很多 bug 出在边缘逻辑里,比如类型隐式转换、空输入、非整数输入。
必须覆盖的用例:
-
factorial(0)→1(数学定义,不是特例) -
factorial(1)→1(最简正向路径) -
factorial("5")或factorial(5.0)—— 如果函数没做类型校验,可能返回意外结果 -
factorial(-3)应明确报错或返回false,不能静默返回1或进入死循环 -
factorial(1000)如果用字符串模拟,要检查内存是否爆、执行时间是否超限
真正难校验的不是大数本身,而是你写的函数在类型、符号、空值上的行为一致性。GMP 只解决精度,不解决逻辑漏洞。











