使用方法
- 在「被开数」填 125(示例值,页面打开时已预填)。这个输入是被开立方的那个数,不是你要的立方根——125 的立方根是 5,8 的立方根是 2,别把两者填反。
- 直接按 Enter,或点「计算立方根」。结果区会同时给出
Math.cbrt的值、Math.pow(x,1/3)的值、两者的 ULP 差距、牛顿迭代实测步数和立方还原误差。 - 填负数试试:−27 会得到
−3,而Math.pow(−27, 1/3)返回 NaN。这是立方体平方根计算器最值得看的一组对照,原理那一节解释了为什么。 - 填 0.001 或 0.5 这类小于 1 的数,观察牛顿迭代步数的变化:小数收敛明显更慢,
0.5那一行甚至会在最后一位上反复跳动。 - 点「导出 CSV」把 12 行对照表带走(带 BOM,Excel 打开中文不乱码)。立方体平方根计算器的每个输入都带范围校验,越界会直接告诉你该填什么。
立方体平方根计算器能解决什么问题
立方根是解「x³ = a」的唯一实数解,工程上叫三次根。它的使用场景集中在四个地方,每个都不是「拿手机计算器敲一下」能解决的。立方体平方根计算器把这四条路径一次算清,顺带把两种算法的差异摆出来。
一是体积反推。已知一个立方体的体积 V,反求边长只能开三次方根。这个方向是单向的:给边长算体积用乘法,给体积找边长才用本页。物理里的「立方厘米转升」也是同一类——1 L = 1000 cm³ = 1000 的立方根,但如果你手上是 2 L 水的体积要换成立方厘米,就得开三次方根。
二是财务与统计里的三次方缩放。立方根是唯一能让「正数、倒数、几何平均」三件事同时自洽的变换方式。当你要把一组偏态数据压到接近正态时,取立方根后再取对数是常见手法;反过来,如果一个指标本身就是「三次方根口径」(比如某些能耗指数),还原原值就要本页。
三是把「多少次方」和「开多少次方」对齐。很多人以为 Math.pow(27, 1/3) 和立方根是一回事,实测它们在 27 上恰好相同,但在 125、64、1000000、1000000000、1000000000000 上全都差 1~2 个 ULP。这个差别平时看不出来,可一旦你做的是「结果再参与三次方还原校验」这种闭环运算,误差会累积。立方体平方根计算器把两个值并排显示就是为了让这件事可核对。
四是判断一个数是不是完美立方。立方根是整数,就说明原数能被某个整数的三次方整除。资源规格配比、魔方体积、立方体容器容量这类场景经常要判这个。页面在结果区会直接告诉你立方还原校验是否精确回到原数。
本页不做这些事:不做平方根(那是另一个口径,指数是 1/2 不是 1/3,负数行为也不同)、不做任意实数次幂的反函数(那要先确定指数是几)、也不做复数域的三次方根——那需要处理实部虚部,Math.cbrt 只给实数解。要算平方根请用「平方根计算器」,它按 Math.sqrt 的口径实现并单独处理负数报错。
原理和变量:cbrt、pow 和牛顿迭代到底差在哪
第一步,Math.cbrt 是规范定义的原生函数。ECMA-262 把它定义为「返回 x 的立方根」,实现上不要求必须用某个特定算法,但要求结果最接近真值(正确舍入)。这和 Math.sqrt 的规范地位完全一样——两者都是 IEEE 754 754-2008 里的推荐操作,指定了正确舍入行为。这也意味着:Math.cbrt(x) 和 Math.pow(x, 1/3) 不是同一个函数,只是「数学上应该相等」。
第二步,Math.pow(x, 1/3) 的问题出在 1/3 本身。在 IEEE 754 双精度下,1/3 存下来的不是 1/3,而是最近的可表示数 0.333333333333333314829616256247(十六进制 0x555555555555D53F),比真值小。拿这个偏小的指数去求对数、再取幂,误差不会被抵消。实测结果:
Math.pow(125, 1/3)= 4.999999999999999,而Math.cbrt(125)= 5,差 8.881784197001252e-16,正好是 5 附近 1 个 ULP;Math.pow(64, 1/3)= 3.9999999999999996,低 1 个 ULP;Math.pow(1000000, 1/3)= 99.99999999999997,Math.pow(1e9, 1/3)= 999.9999999999997,两者都低 2 个 ULP;Math.pow(1e12, 1/3)= 9999.999999999995,低 2 个 ULP。
有意思的是,27、8、2、3、0.5、0.001 这几个输入上两者逐位相同。这不是巧合——当结果的 ULP 尺度恰好与指数的表示误差「错开」时,误差会掉进舍入的缝隙里。所以判断标准很简单:不要用「哪个看起来更整齐」判断,要用 ULP 计数判断,本页表格里就是按 ULP 关系给的。
第三步,负数为什么返回 NaN。Math.pow 的规范定义是:底数为负、指数为非整数时返回 NaN。而 1/3 恰好是非整数——即使它的值最接近 1/3,IEEE 754 也会把它当成「不是整数」。Math.pow(−8, 1/3)、(−8) ** (1/3) 实测都是 NaN,只有 Math.cbrt(−8) = −2。要处理负数的三次方根,必须走 Math.cbrt 或先取绝对值再补符号,没有第三条路。
第四步,牛顿迭代法 x(n+1) = (2·x(n) + a / x(n)²) / 3。这个式子来自对 f(x) = x³ − a 求导并作切线,取 f(x)=0 的交点。它的收敛阶是二阶——每迭代一次,有效位数的平方增长。以 a = 2、初值 x0 = 1 为例,实测相对误差序列是:
5.827e-2 → 3.149e-3 → 9.876e-6 → 9.754e-11 → 0
误差从 5.8% 到 3.1e-3 只用了 1 步(降了约 18 倍),再到 9.9e-6 又降了约 318 倍,再到 9.8e-11 降了约 1 万倍——「平方增长」的实测特征就是这么明显。这就是为什么牛顿法只要 4~5 步就能拿到接近双精度极限的结果,而二分法要 50 步以上(每次只快 1 位)。
问题出在初值上:当 x(n) 远大于真实立方根时,a / x(n)² 会小到可以忽略,牛顿步退化成 x ← 2x/3 + 1 的线性下降,二阶收敛完全发挥不出来。实测 a = 1e-308、x0 = 1 要 590 步才收敛(从 1 降到 1e-103 量级,光单调递减就要 log(1e-103) / log(1.5) ≈ 585 步,之后才有二阶加速)。
第五步,初值的选择决定步数,差距是 10 倍量级。本页用 x0 = 2^round(log₂|a| / 3) 做量级估计初值——这是牛顿法开立方根的标准做法,因为 2 的整数次幂的立方根恰好是 2 的三分之一次幂,估计值天然落在正确量级。实测 12 行数据全部在 1~6 步内收敛。作为对比,同样这些输入用 x0 = 1 需要 8~50 步:a = 1e9 用 x0 = 1 要 38 步、用 x0 = 1024 只要 5 步;a = 1e12 用 x0 = 1 要 50 步、用 x0 = 8192 只要 6 步。
真正的陷阱是 x0 = 0:首步就要算 a / 0 = Infinity,此后永远停在 Infinity 出不来——不是死循环(不会卡住 CPU),是静默停在错误答案上,这类 bug 最难发现。
第六步,a = 0 必须单独处理。牛顿式在 a = 0 时退化成 x ← 2x/3,永远到不了 0:实测跑到 2000 步终值还在 9.00e-89 量级,而 Math.cbrt(0) 直接给 0。所以页面遇到 0 只走 cbrt 分支,不让牛顿法参与。
第七步,为什么页面主用 Math.cbrt 而不是自己写牛顿法。因为牛顿法在双精度下有最后一位 2-循环问题。实测 a = 0.5:迭代到第 6 步后在 0.7937005259840997(十六进制 0x3FE965FEA53D6E3C)和 0.7937005259840998(0x3FE965FEA53D6E3D,即 Math.cbrt(0.5) 的值)之间来回跳动,永不收敛,跑满 2000 步还在跳。这不是 bug,是浮点舍入的固有现象:两个相邻双精度数的立方分别略大于和略小于 0.5,牛顿步把它们互相推过去。此外 a = 3 的终值偏高 1 个 ULP、a = 0.001 的终值偏低 1 个 ULP。所以页面用 Math.cbrt 负责出数,牛顿法只用来演示收敛行为——这两件事不该混在一个函数里。
第八步,一个反直觉的方向性事实:pow 并不总是偏小。前面列的 6 个输入里 pow 都比 cbrt 小,但把被开数换到极小或极大区间,方向会反过来:a = 1e-308 时 cbrt = 2.1544346900318836e-103,而 pow = 2.1544346900319118e-103,pow 反而高了 114 个 ULP;a = 1e-300 时 pow 高 100 个 ULP;而 a = 1e300 时 pow 又反过来低 66 个 ULP。这说明「1/3 偏小所以 pow 结果偏小」这个推理只在中间量级成立——因为 Math.pow 内部是「先取对数、再乘指数、最后取幂」,对数自身的舍入误差会随被开数量级被放大或抵消。结论不变:要正确舍入的立方根就用 Math.cbrt。
完整示例(数值仅作演示)
示例条件与页面打开时的默认值完全一致:被开数 125。结果区在打开时就已经显示着下面这组数,点一次「计算立方根」得到完全一样的结果。
Math.cbrt(125)= 5(双精度十六进制0x4014000000000000,即精确值);Math.pow(125, 1/3)= 4.999999999999999,低 1 个 ULP,差值 8.881784197001252e-16;- 牛顿迭代
x0 = 2^round(log₂125/3) = 4实测 6 步收敛,终值与Math.cbrt(125)逐位相同(若用x0 = 1则需 12 步); - 立方还原校验:
5³ = 125,与输入完全一致(差 0); - 换成
a = 999999999:cbrt= 999.9999996666667,pow= 999.9999996666663,差 3.410605131648481e-13; - 换成
a = 0.5:cbrt= 0.7937005259840998,牛顿法在最后一位 2-循环,跑满 2000 步仍未收敛; - 换成
a = 1e-308:cbrt= 2.1544346900318836e-103,pow= 2.1544346900319118e-103,此时 pow 偏高 114 个 ULP,方向与中间量级相反。
输入范围与对照表
被开数允许的范围与它的物理含义一起给出:
| 输入项 | 页面允许范围 | 步长 | 为什么这么限 |
|---|---|---|---|
| 被开数(正数) | 1e-100 ~ 1e100 | 任意 | 覆盖从「一粒沙的立方」到「地球的立方」的全部尺度;上限 1e100 的立方 1e300 仍在双精度范围内 |
| 被开数(负数) | −1e100 ~ −1e-100 | 任意 | Math.cbrt 支持负数;Math.pow(x,1/3) 会返回 NaN,所以本页走 cbrt 路径 |
| 被开数(0) | 0 | — | 允许,但 1/Math.cbrt(0) 会得到 Infinity,做倒数运算时要先判零 |
| 展示精度 | 6 位小数 / 12 位有效数字 | 固定 | 结果 ≥ 1000 时自动改用有效数字,否则 999.9999996666667 会被四舍五入成 1000.000000 |
下表把 12 组被开数的三种算法结果并排。「ULP 关系」列是本页的核心判据——「低 1 ULP」的含义是:两个双精度数在最后一个二进制位上差 1,即在 5 附近差 8.881784197001252e-16。ULP 计数是用 BigInt64Array 读位模式、逐档下移数出来的,不是估算值;牛顿步数列用的是 x0 = 2^round(log₂|a|/3):
| 行 | 被开数 | Math.cbrt(12 位有效数字) | Math.pow(x,1/3) | ULP 关系 | 初值 x0 | 牛顿步数 | 立方还原 |
|---|---|---|---|---|---|---|---|
| 1(默认) | 125 | 5.00000000000 | 4.999999999999999 | 低 1 ULP | 4 | 6 | 精确 |
| 2 | 27 | 3.00000000000 | 3 | 逐位相同 | 4 | 6 | 精确 |
| 3 | 8 | 2.00000000000 | 2 | 逐位相同 | 2 | 1 | 精确 |
| 4 | 64 | 4.00000000000 | 3.9999999999999996 | 低 1 ULP | 4 | 1 | 精确 |
| 5 | 1000000 | 100.000000000 | 99.99999999999997 | 低 2 ULP | 128 | 6 | 精确 |
| 6 | 2 | 1.25992104989 | 1.2599210498948732 | 逐位相同 | 1 | 6 | 精确 |
| 7 | 3 | 1.44224957031 | 1.4422495703074083 | 逐位相同 | 2 | 6 | 差 8.88e-16 |
| 8 | 0.001 | 0.100000000000 | 0.1 | 逐位相同 | 0.125 | 6 | 差 2.17e-19 |
| 9 | 1000000000 | 1000.00000000 | 999.9999999999997 | 低 3 ULP | 1024 | 5 | 精确 |
| 10 | 1000000000000 | 10000.0000000 | 9999.999999999995 | 低 3 ULP | 8192 | 6 | 精确 |
| 11 | 999999999 | 999.999999667 | 999.9999996666663 | 低 3 ULP | 1024 | 5 | 精确 |
| 12 | 0.5 | 0.793700525984 | 0.7937005259840998 | 逐位相同 | 1 | 500(2-循环) | 差 1.11e-16 |
四个值得停下来看的行:第 1 行最反直觉——125 的立方根在数学上就是整数 5,cbrt 给的是精确的 5,而 pow 给了一个「差一位」的 4.999999999999999。第 3、4 行对照着看很说明问题:8 的立方根 cbrt 与 pow 逐位相同且牛顿法 1 步命中(初值 x0 = 2 恰好就是答案),而 64 的立方根 pow 低 1 个 ULP。第 9、10 行说明被开数越大,ULP 的绝对差也越大(1e9 处是 3.4106e-13,1e12 处是 5.4570e-12),但相对差始终在 1e-16 量级。第 12 行是唯一「两者相同但牛顿法失败」的情况——cbrt 和 pow 都对,牛顿法却卡在 2-循环里,这正好印证了「不要用自己写的迭代法替代原生函数」。
边界与精度
- 溢出:立方根几乎不会溢出,但立方还原会。
Math.cbrt(1e300)=1e100,实测有效;但把这个结果再立方回去会得到1e300(刚好有效),而cbrt(1e308)=4.641588833612779e102,立方回去就是Infinity。同样Math.cbrt(Number.MAX_VALUE)=5.643803094122362e102,立方还原必然溢出。所以「用立方根算完再立方校验」这个闭环,在 |a| > 1e100 时会失效——不是 cbrt 算错了,是还原那一步先溢出了。 - 下溢:极小数会静默归零,这是本页唯一真正需要提防的地方。
Math.cbrt(1e-308)=2.1544346900318836e-103,但如果用toFixed(6)展示,会得到 0.000000。而Math.cbrt(1e-300)=1e-100,toFixed(6)同样显示 0.000000。这就是为什么页面在结果绝对值小于 1e-6 时自动改用有效数字格式——否则你会以为页面算错了。 - 四舍五入掩盖真值:999999999 是最好的例子。真值是
999.9999996666667,但toFixed(6)给你1000.000000。你会以为 999999999 的立方根是整数 1000,实际上 1000³ = 1e9 ≠ 999999999。页面在 |结果| ≥ 1000 时改用toPrecision(12),这一行的显示值是999.999999667,一眼就能看出不是整数。 - 0 与 −0 是两个不同的值。
Math.cbrt(0)返回+0,Math.cbrt(-0)返回−0,用Object.is一测便知。这在除法里会体现为Infinity与-Infinity的差别,某些统计代码里会造成符号错误。 - 精度上限:相对误差约 1.11e-16(2⁻⁵³)。双精度有 53 位有效尾数,
Number.EPSILON=2.220446049250313e-16= 2⁻⁵²,而正确舍入的相对误差上界是半个 ULP 即 2⁻⁵³ = 1.1102230246251565e-16。本页所有差异都在这个量级以内,如果你看到比这大得多的误差,那是输入本身就错了,不是算法的问题。 - 精度与安全整数无关。
Number.MAX_SAFE_INTEGER=9007199254740991是整数运算的精确上限,但立方根是浮点开方,不受它约束——cbrt(1e300)依然有效。两者是不同的精度概念,不要混用。
常见问题
为什么立方根计算器能算负数,而 pow(x, 1/3) 会返回 NaN?
因为 IEEE 754 对 Math.pow 的规定是:底数为负、指数为非整数时结果无定义,返回 NaN。这里的「非整数」是按存储值判断的——1/3 在双精度下的值是 0.333333333333333314829616256247,IEEE 754 明确把它归类为「非整数」,哪怕它离 1/3 只差了 1.85e-17。Math.cbrt 走的是完全不同的规范路径,它对负数直接返回对应实数根。实测 Math.pow(−8, 1/3) = NaN、(−8) ** (1/3) = NaN、Math.pow(−27, 1/3) = NaN,而 Math.cbrt(−8) = −2、Math.cbrt(−27) = −3。如果你的代码里出现 Math.pow(x, 1/3) 并且需要处理负数,那是个明确的 bug,请换成 Math.cbrt(x),或者自己写 sign(x) * Math.pow(Math.abs(x), 1/3)——但后者会重新引入 ULP 偏差,不如直接用 cbrt。
立方根和平方根只差一个指数,为什么不能合并成一个工具?
因为两者的负数行为和规范地位都不同。平方根这边 Math.sqrt(−1) 返回 NaN,而立方根 Math.cbrt(−1) 返回 −1;Math.sqrt(x) 与 Math.pow(x, 0.5) 实测在 2、3、10、1e300、0.1、1e-300 上全部逐位相同(因为 0.5 是二进制可精确表示的数,1/3 不是——这是关键差别),但 Math.sqrt 额外规定了「−0 输入返回 −0」和 +∞ 的行为。合并成一个页面,你就没法把「为什么 64 的平方根用 pow 算也对,但立方根用 pow 算就差一位」这件事讲清楚。站内也已有独立的平方根计算器页处理 0.5 指数那一套,两页各讲一件事更好。
牛顿迭代法明明二阶收敛,为什么页面不用它出数?
因为它在双精度下有最后一位 2-循环这个无法用「多迭代几次」解决的行为。实测 a = 0.5、x0 = 1:迭代到 0.7937005259840997(0x3FE965FEA53D6E3C)和 0.7937005259840998(0x3FE965FEA53D6E3D)之间来回跳,跑满 2000 步仍未停。原因是这两个相邻双精度数的立方分别略小于和略大于 0.5,牛顿步把它们互相推过去,永远落不到不动点上。此外 a = 3 的终值偏高 1 个 ULP、a = 0.001 的终值偏低 1 个 ULP。要打破 2-循环必须加额外判断(比如检测连续两次反向跳变就取均值,或改用 Halley 方法),但这时你写的已经不是「牛顿法」了,而 Math.cbrt 一步到位且规范保证正确舍入。页面仍然实现牛顿法,是为了让你看到收敛速度实测数据和这个 2-循环现象——这两件事的教学价值远高于省几行代码。
初值选多少合适?为什么 x0=0 会死循环?
x0 = 0 时首步要算 a / x(n)² = a / 0 = Infinity,然后 (2×Infinity + Infinity) / 3 还是 Infinity,之后永远卡在 Infinity——不是死循环(不会卡住 CPU),是静默停在错误答案上,这类 bug 最难发现。安全初值的取舍实测如下:固定 x0 = 1 对所有正数都保证收敛,代价是慢(a = 1e12 要 50 步、a = 1e-308 要 590 步);按量级估计取 x0 = 2^round(log₂|a|/3) 则 12 行样本全部 1~6 步收敛,a = 1e9 用 x0 = 1024 只需 5 步、a = 1e12 用 x0 = 8192 只需 6 步,比 x0 = 1 快近 10 倍。另外要注意 a = 0 是特例:牛顿式退化成 x ← 2x/3,跑到 2000 步也到不了 0,只能靠 cbrt。
结果 999.9999996666667 为什么会被显示成 1000.000000?
因为 toFixed(6) 的语义是「保留 6 位小数并四舍五入」,999.9999996666667 的第 7 位小数是 6,进位后就成了 1000.000000。这是显示问题,不是计算问题——Math.cbrt(999999999) 返回的确实是 999.9999996666667,Object.is 一比就知道。如果你的业务里「立方根是不是整数」是个判断条件(比如判断某数是否为完全立方数),千万不要用 toFixed(6) === '1000.000000' 这种字符串比较,那会把 999999999 误判成完全立方数。正确做法是直接比较原始值:const r = Math.cbrt(n); const isPerfectCube = r * r * r === n;。本页在 |结果| ≥ 1000 时改用 toPrecision(12),就是为了让这类误判在界面上就暴露出来。
立方还原校验显示「差 8.88e-16」,是不是说明 cbrt 算错了?
不是,这正是双精度运算的正常表现。看表里第 7 行:cbrt(3) = 1.4422495703074083,立方还原得 2.9999999999999996,与原数差 8.88e-16。但同一行的「牛顿步数」列显示 cbrt 和 pow 是逐位相同的——也就是说这个误差不是 cbrt 引入的,而是「立方」这一步自身的舍入误差:1.4422495703074083 立方时的中间结果已经无法精确表示了。判断标准很简单:差异是否在 1e-15 量级以内。2⁻⁵³ = 1.11e-16 是正确舍入的相对误差上界,几个 ULP 累加起来到 1e-15 属于正常。如果差异到了 1e-10 甚至更大,那才说明算法或输入有问题。
1e-308 的立方根是 0 吗?
不是。Math.cbrt(1e-308) = 2.1544346900318836e-103,是一个完全正常的双精度数。它的立方还原也正好是 1e-308。显示成 0 是格式问题:toFixed(6) 对小于 0.0000005 的数一律返回 0.000000,因为它只保留 6 位小数。页面在结果绝对值小于 1e-6 时自动切换到 toPrecision(12),这时 cbrt(1e-308) 会显示为 2.15443469003e-103。判断极小数是否为 0 要用 === 0 而不是看格式化后的字符串——格式化会骗你,这是所有「显示层污染计算层」的经典案例。
算出来的立方根能直接拿去算钱、开方吗?
能,但要看你做什么。如果是财务计算,务必注意 cbrt 是浮点开方,误差量级 1e-16;按分取整时 Math.cbrt 的结果很可能会让一个「本该整除」的值变成 99.99999999999997,Math.round 之后正好回到 100,看起来没事,但如果你做 === 100 的等值判断就会失败。如果是几何或工程,1e-16 的相对误差完全可忽略;但要注意溢出边界——|被开数| > 1e100 时立方还原会先溢出成 Infinity,闭环校验失效。如果涉及资金结算,建议先用 Math.cbrt 得到参考值,再用精确有理数运算或整数分(以「分」为单位存钱)做最终确认。页面给出的所有数字都标注了「仅作演示」,不构成任何报价或结算依据。
相关工具
- 平方根计算器——按
Math.sqrt口径算二次方根,指数是 0.5(可精确表示)所以与Math.pow(x,0.5)逐位相同,负数会返回 NaN 并单独报错。 - 体积立方体计算器——给边长算立方体体积,走的是乘法方向;本页是它的反函数,两页配合可以「给体积反推边长」。
- 体积长方体计算器——处理非正方体的情况,长宽高三个维度不齐时不能只用一个数开方。
- 圆柱体积计算器——圆柱体积 = πr²h,反推尺寸时同样是开方问题,但底面积那一项要先除 π 再开平方。
- 指数计算器——算 aᵇ 的正向幂运算;本页是它的逆运算(指数固定为 1/3),两者配合可以互相校验。
- 对数计算器——如果你的场景是「指数不是 3」的可变次方反函数,对数是更通用的工具:先取对数、再除以指数。
- 科学计数法记数法计算器——处理极小与极大数的读写,避免像
1e-308这样的输入在手工书写时丢精度。