为什么Math.cbrt(1728)比Math.pow(1728,1/3)产生更准确的结果? [英] Why does Math.cbrt(1728) produce a more accurate result than Math.pow(1728, 1/3)?

查看:215
本文介绍了为什么Math.cbrt(1728)比Math.pow(1728,1/3)产生更准确的结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在JavaScript中, Math.cbrt(1728)会计算 12 的确切结果。然而,表面上相当的表达式 Math.pow(1728,1/3)的计算结果为<$ c

为什么这些结果的精确度有所不同?

解决方案

前面几个一般的评论:


  1. 正如在精确的

    由于有限的精度和范围限制,浮点运算
    与实际数学(例如,缺乏关联性的
    )有很大的不同,在浮点运算中计算时,数学上等价的表达式不是
    必然是等价的。

    计算机语言的标准通常不保证数学函数的特定准确性,或者不同数学函数之间相同的误差边界
    ,如 cbrt() pow()。但是,
    数学库提供给定
    精度的正确舍入结果确实存在,例如
    cbrt(x)
  2. 即使两个函数对所有输入正确舍入,也会比 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( )提供正确舍入的结果。对于问题中的示例,正​​确舍入的结果是

    $ $ p code $ cbrt(1728.0)= 1.2000000000000000e + 1(0x1.8000000000000p + 3)
    pow(1728.0,1.0 / 3.0)= 1.1999999999999998e + 1(0x1.7ffffffffffffp + 3)

    也就是 pow()的结果是一个 ulp 小于 cbrt()的结果。对于幅度大的争论,差距会更大。例如,如果 x 是2 <1022 ,则相应的结果相差94 ulps:

    < pre $ 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),但是使用替代的计算方案。虽然实现方式有所不同,但常用的方法是从最初的低精度近似开始,接着是一个或几个步骤哈雷方法,它具有三次收敛性。

    根据经验,当计算机语言提供专用的立方根功能和一般指数函数,前者应该优于后者用于计算立方根。


    In JavaScript, Math.cbrt(1728) evaluate to the exact result of 12.

    However, the seemingly-equivalent expression Math.pow(1728, 1/3) evaluates to 11.999999999999998.

    Why do these results vary in precision?

    解决方案

    A couple of general remarks up front:

    1. As explained in this seminal paper, due to finite precision and range limits, floating-point arithmetic is sufficiently different from real mathematics (for example, lack of associativity) that mathematically equivalent expressions are not necessarily equivalent when evaluated in floating-point arithmetic.

    2. Standards for computer languages do not typically guarantee any particular accuracy for math functions, or identical error bounds between different math functions such as cbrt() or pow(). But math libraries that deliver correctly rounded results for a given precision do exist, such as CRlibm.

    In this case however, cbrt(x) will deliver more accurate results than pow(x,1.0/3.0) even when both functions are correctly rounded for all inputs.

    The issue is that 1.0/3.0 cannot be represented exactly as a floating-point number, whether in binary or decimal. The IEEE-754 double precision number closest to one third is 3.3333333333333331e-1 (or 0x1.5555555555555p-2 when expressed in the C/C++ hexadecimal floating-point format). The relative representational error is -5.5511151231257827e-17 (-0x1.0000000000000p-54), meaning the best double-precision representation of 1/3 is somewhat smaller than the desired mathematical value.

    This initial error in one of the inputs of pow() is not only passed through to the output, it is magnified due to the error magnification property of exponentiation. As a result, pow(x,1.0/3.0) will generally deliver results that are too small compared to the desired cube root, even if pow() delivers correctly rounded results. For the example in the question, the correctly rounded results are

    cbrt(1728.0)        = 1.2000000000000000e+1  (0x1.8000000000000p+3)
    pow(1728.0,1.0/3.0) = 1.1999999999999998e+1  (0x1.7ffffffffffffp+3)
    

    that is, the result from pow() is one ulp smaller than the result from cbrt(). For arguments large in magnitude, the difference will be much larger. For example, if x is 21022, the respective results differ by 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)
    

    The relative error in the result of pow() in this example is 1.3108e-14, demonstrating the magnification of the relative error mentioned above.

    For reasons of both accuracy and performance, math libraries that implement cbrt() therefore typically do not map cbrt(x) to pow(x,1.0/3.0) but use alternative computational schemes. While implementations will differ, a commonly used approach is to start with an initial low-precision approximation followed by one or several steps of Halley's method which has cubic convergence.

    As a rule of thumb, when a computer language offers both a dedicated cube root functionality and general exponentiation functionality, the former should be preferred to the latter for the computation of cube roots.

    这篇关于为什么Math.cbrt(1728)比Math.pow(1728,1/3)产生更准确的结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆