我有一个使用newton-raphson算法计算浮点平方根除法的算法。我的结果不完全准确,有时偏离1 ulp。
我想知道是否存在浮点除法的细化算法以获得最终的精确位。我使用tuckerman测试平方根,但是有一个类似的划分算法吗?或者tuckerman测试可以适应分裂吗?
我也试过使用这个算法,但没有得到完全准确的结果:
z= divisor
r_temp = divisor*q
r = dividend - r_temp
result_temp = r*z
q + result_temp
答案 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;
}