当x为NaN或inf时,为什么ceil实现返回x + x?

cse*_*gen 5 c glibc ieee-754 floor

我正在阅读glibc中的IEEE-754数学函数的实现。这是floor实现。

float
__floorf(float x)
{
    int32_t i0,j0;
    uint32_t i;
    GET_FLOAT_WORD(i0,x);
    j0 = ((i0>>23)&0xff)-0x7f;
    if(j0<23) {
        if(j0<0) {
        /* return 0*sign(x) if |x|<1 */
        if(i0>=0) {i0=0;}
        else if((i0&0x7fffffff)!=0)
          { i0=0xbf800000;}
        } else {
        i = (0x007fffff)>>j0;
        if((i0&i)==0) return x; /* x is integral */
        if(i0<0) i0 += (0x00800000)>>j0;
        i0 &= (~i);
        }
    } else {
        if(__builtin_expect(j0==0x80, 0)) return x+x; /* inf or NaN */
        else return x;      /* x is integral */
    }
    SET_FLOAT_WORD(x,i0);
    return x;
}
Run Code Online (Sandbox Code Playgroud)

有趣的是if(__builtin_expect(j0==0x80, 0)) return x+x; /* inf or NaN */。为什么x+xxinf或NaN 时返回?为什么不回去x呢?

编辑

我从https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/sysdeps/ieee754/flt-32/s_floorf.c获取了代码,并假定它是来自glibc的分支。

Eri*_*hil 5

目的是引发异常。当to的输入floor是信令NaN时,例程应引发浮点无效操作异常。1而不是调用某些通过操纵浮点状态寄存器中的位来执行此操作的例程,而是更容易简单地求值x+x,因为向自身(或任何事物)添加信令NaN会引发无效操作异常。

这在数学库例程的实现中非常普遍。再举一个例子,考虑sin(x)。对于非常小的值xsin(x)是这么近xx在浮点格式的最接近的值表示的,所以返回的值应该是x。但是确切的数学正弦x并不完全正确x(如果x不为零),因此应提出不精确的异常。为此,例程可能会返回例如x + x*x。当x它很小(但不为零)时,其结果将与相同,x但将引发无效异常。

在这种情况下,请注意一个额外的好处:当x为零时,x + x*x不会引发不精确的异常。因此,该表达式适用于零和非常小的非零情况。因此,它不仅替代手动引发异常,而且替代基于x零的分支。在这些表达式中并不少见。它们是实现功能的有效方法。

脚注

1 浮点异常不是C ++异常。如何处理它们取决于浮点环境的设置。最常见的是,它们只是引发程序以后可以检查的标志。但是它们也会引起陷阱,从而改变程序的执行,例如C ++异常。