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

问题描述 投票:5回答:2

我在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;
}

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

编辑

我从https://github.com/lattera/glibc/blob/895ef79e04a953cac1493863bcae29ad85657ee1/sysdeps/ieee754/flt-32/s_floorf.c得到了我的代码,并假设它是来自glibc的fork。

c glibc ieee-754 floor
2个回答
5
投票

目的是提出例外。当floor的输入是信令NaN时,例程应该引发浮点无效操作异常.1而不是通过操作浮点状态寄存器中的位调用一些会执行此操作的例程,更容易简单地评估x+x ,因为向自身(或任何东西)添加信令NaN将引发无效操作异常。

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

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

脚注

1浮点异常不是C ++异常。它们的处理方式取决于浮点环境的设置。最常见的是,他们只是举起程序可以稍后检查的标志。但它们也可能导致陷阱改变程序执行,如C ++异常。


0
投票

我不确定,但我认为它与异常处理有关:如果x太大,那么return x+x可能会产生异常。虽然只是return x根本不会引起任何异常,你可能认为一切都好,而事实上你正在使用错误的数字,或者因为它们超出了正常的编号范围(inf),或者因为它们只是错(NaN)。

© www.soinside.com 2019 - 2024. All rights reserved.