如何细化浮点除法结果?

时间:2015-02-21 00:25:15

标签: math floating-point division ieee-754

我有一个使用newton-raphson算法计算浮点平方根除法的算法。我的结果不完全准确,有时偏离1 ulp。

我想知道是否存在浮点除法的细化算法以获得最终的精确位。我使用tuckerman测试平方根,但是有一个类似的划分算法吗?或者tuckerman测试可以适应分裂吗?

我也试过使用这个算法,但没有得到完全准确的结果:

z= divisor
r_temp = divisor*q
 r = dividend - r_temp
result_temp = r*z
q + result_temp

1 个答案:

答案 0 :(得分:1)

正确舍入迭代除法结果的一种实用方法是在数学结果的一个ulp内产生初步商,然后使用精确计算的残差来计算最终结果。

残差的精确计算的首选工具是融合乘法加法(FMA)运算。这种方法的许多基础工作(无论是在数学方面还是在实际实施方面)都归功于Peter Markstein,后来被其他研究人员所完善。 Markstein的结果很好地总结在他的书中:

Peter Markstein,IA-64和基本功能:速度和精度。 Prentice-Hall 2000。

使用Markstein方法进行正确舍入除法的直接方法是先计算一个正确舍入的倒数,然后通过乘以它来计算正确舍入的商。 股息,然后是最终的基于剩余的舍入步骤。

残差可以用来直接计算最终的舍入结果,如下面代码中的商舍入所示(我注意到这个代码序列导致一个输出错误的舍入结果10个 11 划分,并用另一个比较和选择成语的实例替换它,这是Markstein使用的技术。或者,它可以用作双侧比较和选择过程的一部分,有点类似于Tuckerman舍入,其在下面的代码中示出为倒数舍入。

关于倒数计算有一点需要注意。许多常用的迭代方法(包括我下面使用的方法),当与Markstein的舍入技术结合使用时,如果除数的尾数完全由1位组成,则会产生错误的结果。

解决这个问题的一种方法是特别对待此案。在下面的代码中,我选择了双边比较和选择方法,它也允许在舍入之前略大于一个ulp的错误,因此无需在倒数迭代中使用FMA。

请注意,我在下面的C代码中省略了对子标准结果的处理,以使代码简洁易懂。我限制自己使用标准C库函数来完成诸如提取浮点操作数的部分,组装浮点数以及应用one-ulp递增和递减等任务。大多数平台都会为这些平台提供具有更高性能的机器特定选项。

float my_divf (float a, float b)
{
    float q, r, ma, mb, e, s, t;
    int ia, ib;

    if (!isnanf (a+b) && !isinff (a) && !isinff (b) && (b != 0.0f)) {
        /* normal cases: remove sign, split args into exponent and mantissa */
        ma = frexpf (fabsf (a), &ia);
        mb = frexpf (fabsf (b), &ib);
        /* minimax polynomial approximation to 1/mb for mb in [0.5,1) */
        r =        - 3.54939341e+0f;
        r = r * mb + 1.06481802e+1f;
        r = r * mb - 1.17573657e+1f;
        r = r * mb + 5.65684575e+0f;
        /* apply one iteration with cubic convergence */
        e = 1.0f - mb * r;
        e = e * e + e;
        r = e * r + r;
        /* round reciprocal to nearest-or-even */
        e = fmaf (-mb, r, 1.0f); // residual of 1st candidate
        s = nextafterf (r, copysignf (2.0f, e)); // bump or dent 
        t = fmaf (-mb, s, 1.0f); // residual of 2nd candidate
        r = (fabsf (e) < fabsf (t)) ? r : s; // candidate with smaller residual
        /* compute preliminary quotient from correctly-rounded reciprocal */
        q = ma * r;
        /* round quotient to nearest-or-even */
        e = fmaf (-mb, q, ma); // residual of 1st candidate
        s = nextafterf (q, copysignf (2.0f, e)); // bump or dent 
        t = fmaf (-mb, s, ma); // residual of 2nd candidate
        q = (fabsf (e) < fabsf (t)) ? q : s; // candidate with smaller residual
        /* scale back into result range */
        r = ldexpf (q, ia - ib);
        if (r < 1.17549435e-38f) {
            /* sub-normal result, left as an exercise for the reader */
        }
        /* merge in sign of quotient */
        r = copysignf (r, a * b);
    } else {
        /* handle special cases */
        if (isnanf (a) || isnanf (b)) {
            r = a + b;
        } else if (b == 0.0f) {
            r = (a == 0.0f) ? (0.0f / 0.0f) : copysignf (1.0f / 0.0f, a * b);
        } else if (isinff (b)) {
            r = (isinff (a)) ? (0.0f / 0.0f) : copysignf (0.0f, a * b);
        } else {
            r = a * b;
        }
    }
    return r;
}