【问题标题】:why floor, ceil implementation return x + x when x is NaN or inf?当 x 为 NaN 或 inf 时,为什么 floor、ceil 实现返回 x + x?
【发布时间】:2019-04-04 05:53:07
【问题描述】:

我正在阅读 IEEE-754 数学函数在 glibc 中的实现。 这是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 是inf 或NaN 时返回x+x? 为什么不直接返回x

编辑

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

【问题讨论】:

  • 代码的格式看起来不是 GNUish。你确定它来自glibc?你应该ask the author of that code,我通过谷歌搜索"float __floorf"找到了他。
  • 您应该在您的问题中添加您搜索答案的链接。知道此代码来自 1993 年的 SUN 可能有助于最终回答这个问题。
  • 对标准不是很熟悉,但 IIRC NaN 可以发出或不发出信号,也许这是为了处理这个问题。从@RolandIllig 的结果进一步搜索,commit adding similar code 到 glibc 说:“地板的 alpha 版本错误地为 sNaN 输入返回 sNaN。通过检查 NaN 并在这种情况下返回与自身相加的输入值来解决这个问题。”跨度>
  • @muru:是的,我认为是设置 FP 异常标志,例如ceil 需要在无限输入上设置或引发溢出,或者在 NaN 输入上无效。
  • 是的,inf 和 NaN 会保持不变,但可能会出现一些副作用,至少对于发出 NaN 信号:浮点异常。

标签: c glibc ieee-754 floor


【解决方案1】:

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

这在数学库例程的实现中很常见。再举一个例子,考虑sin(x)。对于非常小的x 值,sin(x) 非常接近x,以至于x 是浮点格式中可表示的最接近的值,因此返回值应该是x。但是确切的数学罪 x 并不完全是 x(如果 x 不为零),因此应该引发 inexact 异常。为此,例程可能会返回,例如,x + x*x。当x 非常小(但不为零)时,它的计算结果与x 相同,但会引发invalid 异常。

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

脚注

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

【讨论】:

    【解决方案2】:

    我不确定,但我相信它与异常处理有关:如果x 太大,那么return x+x 可能会产生异常。虽然只是return x 根本不会导致任何异常,并且您可能认为一切正常,但实际上您正在使用错误的数字,或者因为它们超出了正常编号范围(inf),或者因为他们完全是错误的 (NaN)。

    【讨论】:

    • NaN 不是数字,因此没有范围。只有 inf 太大
    • @phuclv:你说得对,我已经相应地调整了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-05
    • 1970-01-01
    • 1970-01-01
    • 2014-02-03
    • 2016-01-13
    • 1970-01-01
    • 2020-09-02
    相关资源
    最近更新 更多