在JavaScript中,没有可用的原生cbrt
方法。从理论上讲,你可以使用这样的方法:
function cbrt(x) {
return Math.pow(x, 1 / 3);
}
然而,这失败了,因为数学中的身份不一定适用于浮点运算。例如,使用二进制浮点格式无法准确表示1/3。
此失败的示例如下:
cbrt(Math.pow(4, 3)); // 3.9999999999999996
随着数量变大,情况会变得更糟:
cbrt(Math.pow(165140, 3)); // 165139.99999999988
是否有任何算法能够计算出几个ULP内的立方根值(如果可能,最好是1个ULP)?
这个问题类似于Computing a correctly rounded / an almost correctly rounded floating-point cubic root,但请记住,JavaScript没有任何更高精度的数字类型可供使用(JavaScript中只有一种数字类型),也没有内置的在cbrt
函数中开始。
答案 0 :(得分:2)
您可以将现有实施(例如this one in C)移植到Javascript。该代码有两个变体,一个更准确的迭代和一个非交互的变体。
Ken Turkowski的实现依赖于将radicand拆分为尾数和指数然后重组它,但这仅用于通过在-2之间强制执行二进制指数将其置于1/8和1之间的范围内进行第一次近似。在Javascript中,你可以通过重复除以或乘以8来做到这一点,这不应该影响准确性,因为它只是一个指数移位。
本文所示的实现对于单精度浮点数是准确的,但Javascript使用双精度数。再添加两次牛顿迭代可以获得良好的精度。
以下是所述cbrt
算法的Javascript端口:
Math.cbrt = function(x)
{
if (x == 0) return 0;
if (x < 0) return -Math.cbrt(-x);
var r = x;
var ex = 0;
while (r < 0.125) { r *= 8; ex--; }
while (r > 1.0) { r *= 0.125; ex++; }
r = (-0.46946116 * r + 1.072302) * r + 0.3812513;
while (ex < 0) { r *= 0.5; ex++; }
while (ex > 0) { r *= 2; ex--; }
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
return r;
}
我没有对它进行过广泛的测试,特别是在非常明确的角落情况下,但测试和与pow
的比较我看起来没问题。表现可能不是那么好。
答案 1 :(得分:1)
Math.cbrt已添加到ES6 / ES2015规范中,因此至少首先检查它是否已定义。它可以像:
一样使用 Math.cbrt(64); //4
而不是
Math.pow(64, 1/3); // 3.9999999999999996
答案 2 :(得分:0)
您可以使用pow computation
的公式x^y = exp2(y*log2(x))
x^(1/3) = exp2(log2(x)*1/3)
= exp2(log2(x)/3)
log,exp
的基数可以是任意数据,但2
直接在大多数FPU上实现或您可以使用位搜索
(e= ~1/3 of integer part bit count of x)
mantissa=0
和exponent=e
)从y
(y*y*y>x)
将位切换回零循环#3 与下一位(在 LSB 之后停止)
二进制搜索的结果尽可能精确(没有其他方法可以击败它)...你需要尾数位计数迭代。您必须使用 FP 进行计算,因此将y
转换为float
只是复制尾数位并设置指数。