algorithm – 计算正确舍入/几乎正确舍入的浮点立方根

假设可以使用正确舍入的标准库函数,例如CRlibm中的函数.那么如何计算双精度输入的正确圆角立方根?

引用FAQ时,这个问题不是“[我]面临的实际问题”.这有点像家庭作业.但立方根是经常发现的操作,可以想象这个问题是某人面临的实际问题.

由于“最好的Stack Overflow问题中有一些源代码”,这里有一些源代码:

  y = pow(x, 1. / 3.);

上面没有计算正确的圆角立方根,因为1/3不能完全表示为double.

补充笔记:

article描述了如何计算浮点立方根,但推荐的Newton-Raphson算法的最后一次迭代必须以更高的精度进行算法计算正确舍入的双精度立方根.这可能是计算它的最佳方法,但我仍然在寻找一种利用现有正确舍入标准化功能的捷径.

C99包含一个cbrt()函数,但不能指望所有编译器都正确舍入or even faithful. CRlibm的设计者可以选择在提供的函数列表中包含cbrt(),但他们没有.欢迎参考其他正确舍入的数学函数库中可用的实现.

我担心我不知道如何保证正确舍入的双精度立方根,但可以提供一个非常接近正确舍入的问题.换句话说,最大误差似乎非常接近0.5 ulp.

Peter Markstein,“IA-64和基本功能:速度和精度”(Prentice-Hall 2000)

提出了有效的基于FMA的技术,用于正确地舍入倒数,平方根和倒数平方根,但在这方面它没有涵盖立方根.一般而言,Markstein的方法需要初步结果,该结果在最终舍入序列之前精确到1ulp之内.我没有足够的数学手段将他的技术扩展到立方根的圆角,但在我看来,原则上这应该是可能的,这是一个有点类似于倒数平方根的挑战.

逐位算法很容易通过正确的舍入来计算根.由于IEEE-754舍入模式的连接情况不能发生,因此只需要进行计算直到产生所有尾数位加上一个舍入位.基于二项式定理的平方根的逐位算法在非恢复和恢复变体中都是众所周知的,并且已经成为硬件实现的基础.通过二项式定理的相同方法适用于多维数据集根,并且有一篇鲜为人知的文章列出了非恢复实现的细节:

H. Peng,“提取平方根和立方根的算法”,Proceedings 5​​th IEEE International Symposium on Computer Arithmetic,pp.121-126,1981.

我可以通过实验来判断它最适合从整数中提取立方根.由于每次迭代只产生一个结果位,因此速度并不快.对于浮点运算中的应用,它具有使用几个簿记变量的缺点,这些变量需要大约两倍于最终结果的位数.这意味着需要使用128位整数运算来实现双精度立方根.

我下面的C99代码基于Halley’s rational method for the cube root,它具有立方收敛,这意味着初始近似不必非常精确,因为每次迭代中有效数字的数量为三倍.可以以各种方式安排计算.通常,将迭代方案安排为数值上是有利的

new_guess:= old_guess更正

因为对于足够接近的初始猜测,校正明显小于old_guess.这导致了立方根的以下迭代方案:

x:= x – x *(x3 – a)/(2 * x3 y)

这种特殊的安排也在Kahan’s notes on cube root中列出.它具有进一步的优点,可以自然地使用FMA (fused-multiply add).一个缺点是2 * x3的计算可能导致溢出,因此至少需要一个参数减少方案双精度输入域的.在我的代码中,我简单地将参数简化应用于所有非异常输入,基于对IEEE-754双精度操作数的指数的直接操作.

区间[0.125,1]用作主要近似区间.使用多项式minimax近似,返回[0.5,1]中的初始猜测.窄范围有助于将单精度算术用于计算的低精度部分.

我无法证明我的实现的错误界限,但是,针对参考实现(精确到大约200位)测试了超过2亿个随机测试向量,检测到没有超过半个ulp的错误,这表明最大错误必须非常接近0.5 ulp.

double my_cbrt (double a)
{
    double b, u, v, r;
    float bb, uu, vv;
    int e, f, s;

    if ((a == 0.0) || isinf(a) || isnan(a)) {
        /* handle special cases */
        r = a + a;
    } else {
        /* strip off sign-bit */
        b = fabs (a);
        /* compute exponent adjustments */
        b = frexp (b, &e);
        s = e - 3*342;
        f = s / 3;
        s = s - 3 * f;
        f = f + 342;
        /* map argument into the primary approximation interval [0.125,1) */
        b = ldexp (b, s);
        bb = (float)b;
        /* approximate cube root in [0.125,1) with relative error 5.22e-3 */
        uu =                0x1.2f32c0p-1f;
        uu = fmaf (uu, bb, -0x1.62cc2ap+0f);
        uu = fmaf (uu, bb,  0x1.7546e0p+0f);
        uu = fmaf (uu, bb,  0x1.5d0590p-2f);
        /* refine cube root using two Halley iterations w/ cubic convergence */
        vv = uu * uu;
        uu = fmaf (fmaf (vv, uu, -bb) / fmaf (vv, 2.0f*uu, bb), -uu, uu);
        u = (double)uu;
        v = u * u; // this product is exact
        r = fma (fma (v, u, -b) / fma (v, 2.0*u, b), -u, u);
        /* map back from primary approximation interval by jamming exponent */
        r = ldexp (r, f);
        /* restore sign bit */
        r = copysign (r, a);
    }
    return r;
}
https://stackoverflow.com/questions/18063755/computing-a-correctly-rounded-an-almost-correctly-rounded-floating-point-cubic

本站文章除注明转载外,均为本站原创或编译
转载请明显位置注明出处:algorithm – 计算正确舍入/几乎正确舍入的浮点立方根