moh*_*med 12 javascript floating-point
在JavaScript中,Math.cbrt(1728)
评估确切的结果12
.
然而,看似等效的表达式Math.pow(1728, 1/3)
评估为11.999999999999998
.
为什么这些结果的精度会有所不同?
nju*_*ffa 18
前面几个一般性评论:
正如本篇论文中所解释的,由于有限的精度和范围限制,浮点算法与实际数学(例如,缺乏关联性)有很大的不同,在浮点运算中求值时,数学上等效的表达式不一定是等价的.
计算机语言的标准通常不保证数学函数的任何特定精度,或者不同数学函数(如cbrt()
或)之间的相同误差界限pow()
.但是,确实存在为给定精度提供正确舍入结果的数学库,例如
CRlibm.
但是,在这种情况下,即使两个函数都针对所有输入正确舍入,cbrt(x)
也会提供更准确的结果pow(x,1.0/3.0)
.
问题是1.0/3.0
无法用二进制或十进制表示为浮点数.最接近三分之一的IEEE-754双精度数是3.3333333333333331e-1(或以C/C++十六进制浮点格式表示时为0x1.5555555555555p-2).相对代表性误差为-5.5511151231257827e-17(-0x1.0000000000000p-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)
Run Code Online (Sandbox Code Playgroud)
也就是说,结果来自pow()
一个小于结果的ulpcbrt()
.对于幅度较大的论点,差异会大得多.例如,如果x
是2 1022,则相应的结果相差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)
Run Code Online (Sandbox Code Playgroud)
该pow()
例子中的结果的相对误差是1.3108e-14,证明了上述相对误差的放大率.
对于这两个精度和性能的原因,实现数学库cbrt()
,因此通常不映射cbrt(x)
到pow(x,1.0/3.0)
,但是使用替代计算方案.虽然实现方式不同,但常用的方法是从初始的低精度近似开始,然后是Halley方法的一个或几个步骤,该方法具有立方收敛性.
根据经验,当计算机语言同时提供专用的立方根功能和一般取幂功能时,前者应优先于后者用于计算立方根.
归档时间: |
|
查看次数: |
534 次 |
最近记录: |