编程语言
首页 > 编程语言> > javascript-实现准确的cbrt()函数而无需额外的精度

javascript-实现准确的cbrt()函数而无需额外的精度

作者:互联网

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函数开始.

解决方法:

您可以将现有的实现(例如this one in C)移植到Javascript.该代码有两种变体,一种是更精确的迭代性,另一种是非交互性的.

Ken Turkowski的实现依赖于将radicand分解为尾数和指数,然后重新组装,但这仅用于通过在-2和0之间执行二进制指数来将其近似为1/8和1之间的范围.在Javascript中,您可以通过重复除以8或乘以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的比较看起来还不错.性能可能不是很好.

标签:floating-point,javascript,algorithm
来源: https://codeday.me/bug/20191121/2054457.html