有没有办法用i387 fsqrt指令进行正确的舍入?

时间:2012-03-13 04:06:51

标签: c assembly floating-point rounding x87

有没有办法用i387 fsqrt指令进行正确的舍入?...

... 除了改变x87控制字中的精确模式 - 我知道这是可能的,但它不是一个合理的解决方案,因为它有令人讨厌的重入类型问题,其中精确模式将是如果sqrt操作被中断,则会出错。

我正在处理的问题如下:x87 fsqrt操作码在fpu寄存器的精度中执行正确舍入(按照IEEE 754)平方根操作,我假设它是扩展的(80位)精度。但是,我想用它来实现高效的单精度和双精度平方根函数,并且结果正确舍入(按照当前的舍入模式)。由于结果具有过高的精度,因此将结果转换为单精度或双精度的第二步再次轮回,可能会留下不正确舍入的结果。

通过一些操作,可以通过偏见来解决这个问题。例如,我可以通过以2的幂的形式添加偏置来避免过度结果的过度精度,该偏置将双精度值的52个有效位强制为63位扩展精度尾数的最后52位。但我没有看到任何明显的方法用平方根做这样的技巧。

任何聪明的想法?

(也标记为C,因为预期的应用程序是C sqrtsqrtf函数的实现。)

3 个答案:

答案 0 :(得分:14)

首先,让我们明白一点:你应该使用SSE而不是x87。 SSE sqrtsssqrtsd说明完全符合您的要求,在所有现代x86系统上均受支持,并且速度也快得多。

现在,如果你坚持使用x87,我会从好消息开始:你不需要为浮动做任何事情。您需要2p + 2位来以p位浮点格式计算正确舍入的平方根。因为80 > 2*24 + 2,单精度的附加舍入将始终正确舍入,并且您具有正确的舍入平方根。

现在是坏消息:80 < 2*53 + 2,所以没有双重精确的运气。我可以建议一些解决方法;这是一个很好的简单的一个。

  1. y = round_to_double(x87_square_root(x));
  2. 使用Dekker(head-tail)产品来计算ab,以便y*y = a + b精确计算。
  3. 计算剩余r = x - a - b
  4. if (r == 0) return y
  5. if (r > 0),允许y1 = y + 1 ulp,并计算a1b1 s.t. y1*y1 = a1 + b1。将r1 = x - a1 - b1r进行比较,并返回yy1,具体取决于具有较小残差的残差(或具有零低位的零,如果残差为数量相等)。
  6. if (r < 0),对y1 = y - 1 ulp执行相同的操作。
  7. 此过程仅处理默认的舍入模式;但是,在定向舍入模式中,简单地舍入到目标格式是正确的。

答案 1 :(得分:3)

好的,我认为我有更好的解决方案:

  1. 以扩展精度(y=sqrt(x))计算fsqrt
  2. 如果最后11位不是0x400,只需转换为双精度并返回。
  3. 0x100-(fpu_status_word&0x200)添加到扩展精度表示的低位字。
  4. 转换为双精度并返回。
  5. 步骤3基于以下事实:当且仅当fsqrt的结果被舍入时,状态字的C1位(0x200)为1。这是有效的,因为由于步骤2中的测试,x不是一个完美的正方形;如果它是一个完美的正方形,y将没有超出双精度的位。

    使用条件浮点运算执行步骤3可能会更快,而不是处理位表示和重新加载。

    这是代码(似乎在所有情况下都有效):

    sqrt:
        fldl 4(%esp)
        fsqrt
        fstsw %ax
        sub $12,%esp
        fld %st(0)
        fstpt (%esp)
        mov (%esp),%ecx
        and $0x7ff,%ecx
        cmp $0x400,%ecx
        jnz 1f
        and $0x200,%eax
        sub $0x100,%eax
        sub %eax,(%esp)
        fstp %st(0)
        fldt (%esp)
    1:  add $12,%esp
        fstpl 4(%esp)
        fldl 4(%esp)
        ret
    

答案 2 :(得分:0)

它可能不是您想要的,因为它没有利用387 fsqrt指令,但在glibc中使用32位整数实现了令人惊讶的高效sqrtf(float)算术。它甚至可以正确处理NaNs,Infs,subnormals - 可以用真正的x87指令/ FP控制字标志来消除这些检查。见:glibc-2.14/sysdeps/ieee754/flt-32/e_sqrtf.c

dbl-64/e_sqrt.c代码不太友好。很难说出一目了然的假设。奇怪的是,库的i386 sqrt[f|l]实现只调用fsqrt,但加载值的方式不同。 SP为flds,DP为fldl

相关问题