使用方法
- 阶乘计算器在「n」里默认填 20(示例值),允许 0~10000 的整数;小数与负的非整数也可以填,会走 Gamma 函数分支。
- 「Stirling 近似对比」默认开启第二项,结果区会多出 Stirling 公式的估算值与相对误差;只想要精确值时把它切成「只显示精确值与位数」。
- 点「开始计算」(或在 n 输入框里直接按 Enter),结果区给出三段:BigInt 精确值、双精度 Number 值与是否一致、Stirling 近似与误差。
- 统计条会同时显示四项:精确值位数、双精度是否精确、Stirling 相对误差、以及结果相当于 2^53−1 的多少倍。
- 正文下方的 n! 对照表列出 0 到 25 的每一档,默认显示前 12 行,点「加载更多」每次多展开 12 行,「导出 CSV」把整张表导成带 BOM 的 UTF-8 文件。
这个阶乘计算器能解决什么问题
一是把「算得出来」和「算得准」分开。用 Number 循环乘到 23 就开始悄悄出错,但结果看上去仍然是一串正常数字,肉眼完全看不出来。本页每次都同时给出 BigInt 精确值与 Number 值,并把两者是否一致写明白,你一眼就知道这次的结果能不能直接抄。
二是算位数而不是算数字。很多场景只关心 n! 有多少位,例如判断一个数组的排列数会不会撑爆字段长度。100! 是 158 位,1000! 是 2568 位,10000! 是 35660 位——这些位数用 Stirling 的对数式就能估,不需要真的把这几万个数字乘出来。
三是给估算结果标一个误差范围。Stirling 公式在很多教材里被当成「近似等于」直接抄,但它的相对误差是有具体数值的:n = 5 时低估 1.6507%,n = 20 时低估 0.4158%,n = 100 时低估 0.0833%。知道这个数量级,你才能判断某一次估算能不能当结论用。
四是把定义域边界讲清楚。0! 为什么等于 1、小数能不能算阶乘、(−1)! 为什么没有定义,这三个问题在查资料时经常被含糊带过。这个阶乘计算器把 0~10000 的整数、正负小数、负整数分别走不同分支,每一条分支的取与舍都写在页面上。
计算原理:BigInt 逐项相乘与 2^53−1 边界
双精度能表示到哪一步。JavaScript 的数字是 IEEE 754 定义的 binary64:1 位符号、11 位阶码、52 位尾数,再加上规格化数隐含的最高位,一共能精确表示 53 位二进制有效数字。由此得到所有绝对值不超过 253 = 9007199254740992 的整数都可以无损表示,其中最大的「安全整数」是 253 − 1 = 9007199254740991,也就是 Number.MAX_SAFE_INTEGER。
但「超过 2^53−1」并不等于「马上丢精度」。把一个阶乘写成 2a × 奇部,其中 a 是 n! 里因子 2 的个数 v2(n!) = ⌊n/2⌋ + ⌊n/4⌋ + ⌊n/8⌋ + …。尾数只需要存下那个奇部,剩下的 2a 交给阶码去表示,所以只要奇部本身不超过 53 位,这个整数就仍然可以被精确表示。逐档算下来是这样的:
- 18! = 6402373705728000,二进制正好 53 位,奇部 37 位,落在安全整数范围内——这是最后一个不超过 253−1 的阶乘。
- 19! = 121645100408832000 首次越过这条线,是 253−1 的 13.5053 倍;可它的奇部只有 41 位,所以仍然是精确的。
- 20! = 2432902008176640000 已经是 253−1 的 270.1064 倍,但 v2(20!) = 18、奇部只有 44 位,依然精确。
- 21!(奇部 48 位)、22!(奇部 51 位)继续精确,22! 的奇部 51 位离 53 位只剩两步。
- 23! = 25852016738884976640000 的奇部有 56 位,超过 53 位,这是第一个无法用双精度精确表示的阶乘:Number 算出来与真值相差 +1572864,正好是 1.5×220。
所以网上流传的那句「Number 从 21! 起就不准了」是错的。21! 和 22! 实测都精确,真正的分水岭在 23!。判断依据是奇部位长,不是「数值超过了 253」这个笼统说法——把 253−1 当成一条非黑即白的线,恰恰是最常见的误用。
BigInt 路径。BigInt 是 ECMAScript 2020 引入的任意精度整数类型,后缀 n 标记字面量。本页的精确值走 let r = 1n; for (let i = 2n; i <= n; i++) r *= i,全程不经过浮点,所以位数再多也不会丢有效数字。n = 0 和 n = 1 时循环体一次都不执行,直接返回 1n,这与 0! = 1 的约定天然吻合。Number 路径的上溢则来得更早:Number 能算出的最大阶乘是 170! = 7.257415615307994×10306,再乘一个 171 就超过 Number.MAX_VALUE = 1.7976931348623157×10308,直接变成 Infinity。也就是说用 Number 算阶乘,从 23! 开始不准、到 171! 直接算不出来。
Stirling 近似。公式是 n! ≈ √(2πn)·(n/e)n,本页用它的对数形式 log10(n!) ≈ 0.5·log10(2πn) + n·(log10n − log10e) 先取对数再加回幂次,避免中间结果上溢。这个主项永远偏小,因为它省略了 1 + 1/(12n) + 1/(288n²) − … 这一串修正因子:n = 5 时低估 1.6507%,n = 20 时 0.4158%,n = 100 时 0.0833%,n = 170 时 0.0490%。误差随 n 单调减小,所以它是估算位数的好工具(100! 有 158 位,1000! 有 2568 位,10000! 有 35660 位),不是算精确值的工具。
0! = 1 与 Gamma 函数。0! = 1 是空积的约定,不是凑出来的数:n! 是「把 1 到 n 的所有整数乘起来」,n = 0 时一个数都不乘,而乘法的单位元是 1。这个约定必须成立,否则组合数 C(n,0) = n!/(0!·n!) 就等于不了 1。正整数阶乘可以用 Gamma 函数推广成 n! = Γ(n+1),其中 Γ(x) = ∫0∞ tx−1e−tdt,且 Γ(1) = 1,与 0! = 1 正好对上。小数就靠它:Γ(0.5) = √π ≈ 1.7724539,所以 (−0.5)! ≈ 1.7724539;Γ(1.5) = √π/2 ≈ 0.8862269,即 0.5! ≈ 0.8862269。唯一的例外是负整数——Γ 在 x = 0, −1, −2, … 处都是极点、函数值发散,所以 (−1)! 没有定义。本页对小数和负的非整数用 Lanczos 近似(g = 7,9 个系数)求 Γ,对负整数直接给出报错文案,而不是返回一个看起来很像的假数字。
完整示例:20!
示例条件与页面默认值完全一致:n = 20,开启 Stirling 对比。计算过程分三步。
- BigInt 精确值:从 2n 乘到 20n,得到 2432902008176640000,共 19 位。
- 双精度核对:Number 循环乘出来的同样是 2432902008176640000,与精确值逐位一致。
BigInt(Number(20!)) === 20n!成立,所以这一档仍然可以用普通数字表示。 - Stirling 估算:log10(20!) ≈ 18.38427,还原成科学计数法是 2.422757×1018;与精确值相比偏小 0.415765%。用它估算位数:⌊18.38427⌋ + 1 = 19 位,与精确值的位数一致。
再看两个对照档。n = 18 时,18! = 6402373705728000 落在 253−1 = 9007199254740991 之内,这是最后一个「安全」的阶乘;Stirling 相对误差 −0.461846%。n = 23 时,精确值是 25852016738884976640000,而 Number 算出来的是 2.585201673888498×1022,两者相差 +1572864——数字串看上去还是很正常,但已经错了,这就是必须用 BigInt 的原因。
输入范围与 n! 对照表
下表列出 n = 0 到 25 的每一档:精确值、位数、是否落在 253−1 之内、Number 能否精确表示、以及 Stirling 主项的相对误差。默认显示前 12 行,可展开或导出 CSV。
| n | n!(精确值) | 位数 | 在 253−1 之内 | Number 精确? | Stirling 相对误差 |
|---|---|---|---|---|---|
| 0 | 1 | 1 位 | 是 | 是 | — |
| 1 | 1 | 1 位 | 是 | 是 | -7.786299 % |
| 2 | 2 | 1 位 | 是 | 是 | -4.049782 % |
| 3 | 6 | 1 位 | 是 | 是 | -2.729840 % |
| 4 | 24 | 2 位 | 是 | 是 | -2.057604 % |
| 5 | 120 | 3 位 | 是 | 是 | -1.650693 % |
| 6 | 720 | 3 位 | 是 | 是 | -1.378030 % |
| 7 | 5040 | 4 位 | 是 | 是 | -1.182622 % |
| 8 | 40320 | 5 位 | 是 | 是 | -1.035726 % |
| 9 | 362880 | 6 位 | 是 | 是 | -0.921276 % |
| 10 | 3628800 | 7 位 | 是 | 是 | -0.829596 % |
| 11 | 39916800 | 8 位 | 是 | 是 | -0.754507 % |
| 12 | 479001600 | 9 位 | 是 | 是 | -0.691879 % |
| 13 | 6227020800 | 10 位 | 是 | 是 | -0.638850 % |
| 14 | 87178291200 | 11 位 | 是 | 是 | -0.593370 % |
| 15 | 1307674368000 | 13 位 | 是 | 是 | -0.553933 % |
| 16 | 20922789888000 | 14 位 | 是 | 是 | -0.519412 % |
| 17 | 355687428096000 | 15 位 | 是 | 是 | -0.488940 % |
| 18 | 6402373705728000 | 16 位 | 是 | 是 | -0.461846 % |
| 19 | 121645100408832000 | 18 位 | 否 | 是 | -0.437596 % |
| 20 | 2432902008176640000 | 19 位 | 否 | 是 | -0.415765 % |
| 21 | 51090942171709440000 | 20 位 | 否 | 是 | -0.396009 % |
| 22 | 1124000727777607680000 | 22 位 | 否 | 是 | -0.378045 % |
| 23 | 25852016738884976640000 | 23 位 | 否 | 否 | -0.361641 % |
| 24 | 620448401733239439360000 | 24 位 | 否 | 否 | -0.346600 % |
| 25 | 15511210043330985984000000 | 26 位 | 否 | 否 | -0.332761 % |
表里加底色的是三档关键值:19! 是第一个超出 253−1 的阶乘,20! 是常见示例同时也是「超出但不丢精度」的代表,23! 是第一个 Number 无法精确表示的阶乘。n = 0 一行的 Stirling 误差写作「—」,因为公式里的 (n/e)n 在 n = 0 处没有意义。
边界与精度
- n 的整数范围是 0~10000。阶乘计算器把上限卡在这里不是因为算不动:10000! 有 35660 位,BigInt 相乘一万次在毫秒级就能完成;上限设在这里是为了不让结果区一次塞进几万个字符把页面卡住。
- 171! 在 Number 里是 Infinity。
Number.MAX_VALUE= 1.7976931348623157×10308,170! = 7.257415615307994×10306 是最后一个能装下的阶乘。所以「用 Number 算阶乘」的有效区间只有 0~170,而且其中 23! 到 170! 之间还有精度问题。 - Number 与 BigInt 的分工。只要 n ≥ 23,Number 的结果就只能当量级看,位数可能对但末几位一定不可信;需要准确值一律看 BigInt 那一行。
- 负整数没有定义。Γ 在 x = 0, −1, −2, … 处有极点,函数值发散,所以 (−1)!、(−2)! 都不是「很大的数」或者「0」,而是不存在。工具会直接拒绝并说明原因。
- 负的非整数可以算。例如 (−0.5)! = Γ(0.5) = √π ≈ 1.7724539,(−1.5)! = Γ(−0.5) = −2√π ≈ −3.5449077。注意结果会交替变号,这是反射公式 Γ(x)Γ(1−x) = π / sin(πx) 带来的。
- 小数的阶乘是推广值,不是整数定义。0.5! 之所以等于 0.8862269,是因为定义了 n! = Γ(n+1);对整数以外的 n,阶乘本身没有「逐个相乘」的原始含义。
- 很大的负数会下溢到 0。比如 (−1000.5)! 的量级已经小于 Number 能表示的最小正数,结果会显示成 0;这是浮点下溢,不是真的等于 0。
- Stirling 的误差恒为负。主项省略的修正因子 1 + 1/(12n) + … 恒大于 1,所以近似值永远偏小;要更准就补上 1/(12n) 这一项,n = 20 时能把误差从 −0.415765% 压到 −0.000831%,n = 100 时从 −0.083298% 压到 −0.000034%。
常见问题
为什么 20! 已经超过 2^53−1 了还是精确的?
因为「超过安全整数范围」和「丢精度」不是同一件事。双精度能精确表示的前提是:把整数写成 2a × 奇部之后,奇部的二进制位数不超过 53。20! 里因子 2 的个数 v2(20!) = 10 + 5 + 2 + 1 = 18,奇部只有 44 位,远小于 53,所以尾数装得下,218 那部分交给阶码表示就行。
那到底从几开始不准?
23!。逐个验证下来:19!(奇部 41 位)、20!(44 位)、21!(48 位)、22!(51 位)都还在 53 位以内,全部精确;23! 的奇部有 56 位,超出后 Number 与真值相差 +1572864,是第一个不精确的阶乘。很多资料写「21! 起就不准」,那个说法不对。
那我平时该用哪种方式算阶乘?
只要 n 超过 20,就用 BigInt:let r = 1n; for (let i = 2n; i <= n; i++) r *= i;。它没有位数上限,只受可用内存限制。只有需要「量级」或者「位数」时,才用 Stirling 的对数式去估,速度快得多而且不会上溢。
170! 之后为什么直接变成 Infinity?
因为 Number 的最大有限值是 Number.MAX_VALUE = 1.7976931348623157×10308,而 170! = 7.257415615307994×10306 已经是最后一个装得下的阶乘,再乘 171 就溢出,按 IEEE 754 的规则变成正无穷。要算 171! 及更大,必须换 BigInt。
Stirling 公式为什么不直接用?它明明误差很小。
误差小是相对的。n = 100 时相对误差 0.083298%,看着不到千分之一,但 100! 的前几位是 9332621544…,而 Stirling 主项算出来是 9324847625…——只有「93」这两位对得上,第三位就已经偏了,后面 156 位全不可信。补上 1/(12n) 修正项之后变成 9332618332…,前 5 位才对上。所以 Stirling 适合判断量级和位数(位数误差不影响结论),不适合给出可以照抄的数值;要精确值就用 BigInt 乘出来,本页两边都给你。
0! 为什么等于 1?
因为阶乘是「把 1 到 n 的整数全部相乘」,n = 0 时一个因子都没有,空积按定义为乘法的单位元,也就是 1。这个约定不是可选的:组合数 C(n,0) = n!/(0!·n!) 要等于 1、Gamma 函数要满足 Γ(1) = 1,都得靠它。本页的 BigInt 循环在 n = 0 和 n = 1 时一次都不执行,直接返回 1n,正是同一个道理。
小数和负数能算阶乘吗?
小数能,但算的是 Gamma 函数的推广值:n! = Γ(n+1)。0.5! = Γ(1.5) = √π/2 ≈ 0.8862269,(−0.5)! = Γ(0.5) = √π ≈ 1.7724539。负整数不能,因为 Γ 在 0 和所有负整数处都是极点,函数值发散;负的非整数则可以,会按反射公式 Γ(x)Γ(1−x) = π / sin(πx) 算出来,结果正负交替。
页面上的「位数」是怎么数出来的?
整数分支直接取 BigInt 字符串的长度,是精确值,不存在估算误差。比如 20! 转成字符串是 19 个字符,所以是 19 位;10000! 有 35660 位。Stirling 的对数式本身也能估位数(⌊log10(n!)⌋ + 1),但那是估计值,本页只在解释原理时用它做对照,表格里的位数一律来自字符串长度。Gamma 分支没有整数位数,统计条会改显示函数值本身。
相关工具
- 指数计算器:算 an 这类幂运算,和阶乘一样会遇到超出双精度上限的问题,可以对照着看。
- 对数计算器:Stirling 公式就是先取对数再还原,想手算 log10(n!) 可以用它核对。
- 平方根计算器:√(2πn) 这一项可以直接拿它算,顺带看浮点开方的舍入表现。
- 科学计数法记数法计算器:把阶乘结果转成 a×10n 的写法,位数和量级一目了然。
- 最小公倍数计算器:n! 一定是 1 到 n 所有整数的最小公倍数的倍数,两个数放一起看能理解阶乘增长有多快。
- 古罗马数字转换器:另一种会撞上表示范围上限的记数法,它最大只能写到 3999,和 171! 变成 Infinity 是同一类边界问题。
- 计算器:查看全部计算器工具。