php用bcadd和bcmul可实现大数阶乘,因原生整型64位仅支持至约9.2×10¹⁸,21!即溢出,须用bcmath扩展以字符串形式逐次bcmul累乘,初值为"1"。

PHP 用 bcadd 和 bcmul 手写大数阶乘
PHP 原生整型在 64 位系统上最大约 9.2×10¹⁸,21! 就已溢出,直接用 $n * $n-1 会得到 0 或科学计数法错误值。必须切换到任意精度计算——bcmath 扩展是默认启用的最稳妥选择。
核心思路:从 1 开始,逐次用 bcmul 累乘,初始值设为 "1"(字符串),每一步都保持操作数为字符串:
function bigFactorial($n) {
if ($n < 0) return "0";
if ($n <= 1) return "1";
$result = "1";
for ($i = 2; $i <= $n; $i++) {
$result = bcmul($result, (string)$i);
}
return $result;
}-
bcmul不接受整数参数,传入前务必强转为string,否则小数值可能被截断(如bcmul("123", 45)在某些 PHP 版本中出错) - 循环上限别用
$i 配合 <code>int类型的$n,超大$n(比如 10⁵)会导致循环本身超时,这不是精度问题,是算法复杂度瓶颈 - 结果始终是字符串,不能用
==直接比数字,要用bccomp($a, $b) === 0
为什么不用 gmp_fact?
gmp_fact($n) 看似更简洁,但它有隐藏限制:参数 $n 必须是整型且不能超过平台 long 范围(通常仍是 2⁶³−1),也就是说,你根本传不进一个“大”$n——gmp_fact(100000) 会因参数溢出失败,而非计算失败。
-
gmp_fact只适合$n 且你确认运行环境 <code>long足够宽的场景 - 它返回的是
GMP 对象,后续做字符串输出需调用gmp_strval(),多一层转换 - 若服务器没启用
gmp扩展(比bcmath更不普及),会直接报Fatal error: Uncaught Error: Call to undefined function gmp_fact()
性能临界点在哪?怎么预估耗时?
纯 bcmath 阶乘的时间复杂度接近 O(n² log n),因为第 k 步乘法的位数约是 O(k log k),而 bcmul 底层是朴素乘法。实测参考(Intel i7-10875H,PHP 8.2):
立即学习“PHP免费学习笔记(深入)”;
-
bigFactorial(10000):约 0.15 秒,结果约 36K 位 -
bigFactorial(50000):约 3.8 秒,结果约 213K 位 -
bigFactorial(100000):约 15 秒,结果约 457K 位
超过 10⁵ 后增长陡峭,不是精度不够,是 PHP 解释器和 bcmath 的 C 实现对超长字符串乘法优化有限。此时应考虑改用 Python(math.factorial 底层用 GMP)、或导出为文本后交由外部工具(如 dc)计算。
容易忽略的边界与配置陷阱
bcmath 默认精度(bcscale)只影响除法、模运算等需要小数位的函数,bcmul 和 bcadd 完全无视它——所以别白费劲调 bcscale(100),它对阶乘毫无作用。
- 确保
bcmath已启用:extension_loaded('bcmath')返回true,否则所有bc*函数不可用 - 输入校验不能只靠
is_int(),用户传来的"100000"是字符串,要先filter_var($n, FILTER_VALIDATE_INT)或ctype_digit()验证再进循环 - 内存限制:计算
10⁶!的结果字符串超 5MB,PHP 默认memory_limit=128M虽够,但若并发高,可能触发Fatal error: Allowed memory size exhausted
真正卡住的往往不是“怎么算”,而是没意识到:当 $n > 10⁵ 时,PHP 已不是最优工具,该换语言或分段导出了。











