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