Javascript 为什么Math.cbrt(1728)比Math.pow(1728,1/3)产生更精确的结果?
在JavaScript中,Javascript 为什么Math.cbrt(1728)比Math.pow(1728,1/3)产生更精确的结果?,javascript,floating-point,Javascript,Floating Point,在JavaScript中,Math.cbrt(1728)计算出12的精确结果 然而,看似等价的表达式Math.pow(1728,1/3)的计算结果为11.999999999998 为什么这些结果在精度上有所不同?前面有几点概述: 如本文所述, 由于有限精度和范围限制,浮点运算 与真实的数学有很大的不同(例如 数学上等价的表达式不是 在浮点运算中求值时必须相等 计算机语言标准通常不保证任何 数学函数的特定精度,或相同的误差范围 在不同的数学函数之间,例如cbrt()或pow()。但是 数学库,为给
Math.cbrt(1728)
计算出12
的精确结果
然而,看似等价的表达式Math.pow(1728,1/3)
的计算结果为11.999999999998
为什么这些结果在精度上有所不同?前面有几点概述:
cbrt()
或pow()
。但是
数学库,为给定目标提供正确的四舍五入结果
精度确实存在,例如
cbrt(x)
将提供比pow(x,1.0/3.0)
更精确的结果,即使这两个函数对所有输入都正确舍入
问题在于1.0/3.0
不能准确地表示为浮点数,无论是二进制还是十进制。最接近三分之一的IEEE-754双精度数字为3.3331E-1(或以C/C++十六进制浮点格式表示时为0x1.55555P-2)。相对表示误差为-5.5511151231257827e-17(-0x1.0000000000000 p-54),这意味着1/3的最佳双精度表示略小于所需的数学值
pow()
的一个输入中的初始错误不仅传递到输出,而且由于指数运算的错误放大特性,它被放大。因此,pow(x,1.0/3.0)
通常提供的结果与所需的立方根相比太小,即使pow()
提供正确的四舍五入结果。对于问题中的示例,正确的四舍五入结果如下
cbrt(1728.0) = 1.2000000000000000e+1 (0x1.8000000000000p+3)
pow(1728.0,1.0/3.0) = 1.1999999999999998e+1 (0x1.7ffffffffffffp+3)
也就是说,来自pow()
的结果比来自cbrt()
的结果小一个。对于数量级较大的参数,差异将大得多。例如,如果x
为21022,则相应的结果相差94 ulps:
x = 4.4942328371557898e+307 (0x1.0000000000000p+1022)
cbrt(x) = 3.5553731598732904e+102 (0x1.965fea53d6e3dp+340)
pow(x,1.0/3.0) = 3.5553731598732436e+102 (0x1.965fea53d6ddfp+340)
本例中pow()
结果的相对误差为1.3108e-14,说明了上述相对误差的放大
出于准确性和性能的考虑,实现cbrt()
的数学库通常不会将cbrt(x)
映射到pow(x,1.0/3.0)
,而是使用替代计算方案。虽然实现方式会有所不同,但常用的方法是从初始低精度近似开始,然后是一个或多个具有立方收敛性的步骤
根据经验,当计算机语言同时提供专用的立方根功能和通用的求幂功能时,在计算立方根时,前者应优先于后者。此问题可能是重复的,但它似乎不是链接问题的重复。1.0/3.0不能准确地表示为浮点数,这意味着
pow()
的输入有错误。幂运算具有放大输入误差的众所周知的特性(即,输出误差大于输入误差)。假设cbrt()
和pow()
都是以类似的最大误差(通常是高质量数学库的情况)实现的,那么pow(x,1.0/3.0)
的计算精度就不如cbrt(x)
。后者通常也会更快。事实上,由于我在前面的评论中指出的问题,一个双精度数学库正确地舍入cbrt()
和pow()
将返回问题中所述的精确结果:cbrt(1728)=1.20000000000e+001 0x1.80000000000p+3;战俘(1728,1.0/3.0)=1.199999999998E+001 0x1.7ffffffffffffp+3
@地毯烟民无理数是任何不能表示为整数比率的实数
两个数字都不是无理数。@LyingOnTheSky:该链接仅指如何添加类似于Math.cbrt
的内容(如果无法从实施本机版本使用了一个不同的函数,该函数应该比简单地执行Math.pow(x,1/3)
更精确。相关问题:您是如何在5分钟内编写此函数的?!你有那种脑-键盘接口吗?@lxg:我没有。在过去的五分钟里,我一直在申请一些小的修正。但是我花了20多分钟才写出来。啊,是我的错……我误解了“xx分钟前回答的”。我需要更多的咖啡。无论如何,大约20分钟仍然令人印象深刻@lxg:我的专业背景包括微处理器浮点单元和数学库的设计,另外,当我要求重新打开问题时,我已经知道我想写什么了。