负 NaN 不是 NaN?

发布于 2024-09-16 14:30:15 字数 394 浏览 10 评论 0 原文

在编写一些测试用例时,某些测试会检查 NaN 的结果。

我尝试使用 std::isnan 但断言失败:

Assertion `std::isnan(x)' failed.

打印 x 的值后,结果发现它是负 NaN (-nan)对于我来说这是完全可以接受的。

在尝试使用 NaN != NaN 并使用 assert(x == x) 的事实之后,编译器帮了我一个“忙”并优化了断言。

我自己的 isNaN 函数也正在被优化。

如何检查 NaN -NaN 是否相等?

While writing some test cases, and some of the tests check for the result of a NaN.

I tried using std::isnan but the assert failes:

Assertion `std::isnan(x)' failed.

After printing the value of x, it turned out it's negative NaN (-nan) which is totally acceptable in my case.

After trying to use the fact that NaN != NaN and using assert(x == x), the compiler does me a 'favor' and optimises the assert away.

Making my own isNaN function is being optimised away as well.

How can I check for both equality of NaN and -NaN?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

抹茶夏天i‖ 2024-09-23 14:30:15

这很尴尬。

编译器(本例中为 GCC)正在优化比较并且 isnan 返回 false 的原因是因为我团队中的某人打开了 -ffast-math< /代码>。

从文档中:

-ffast-math
    Sets -fno-math-errno, -funsafe-math-optimizations, -fno-trapping-math,
    -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and
    fcx-limited-range.

    This option causes the preprocessor macro __FAST_MATH__ to be defined.

    This option should never be turned on by any -O option since it can result in
    incorrect output for programs which depend on an exact implementation of IEEE
    or ISO rules/specifications for math functions. 

注意结尾句子 - -ffast-math 是不安全的。

This is embarrassing.

The reason the compiler (GCC in this case) was optimising away the comparison and isnan returned false was because someone in my team had turned on -ffast-math.

From the docs:

-ffast-math
    Sets -fno-math-errno, -funsafe-math-optimizations, -fno-trapping-math,
    -ffinite-math-only, -fno-rounding-math, -fno-signaling-nans and
    fcx-limited-range.

    This option causes the preprocessor macro __FAST_MATH__ to be defined.

    This option should never be turned on by any -O option since it can result in
    incorrect output for programs which depend on an exact implementation of IEEE
    or ISO rules/specifications for math functions. 

Notice the ending sentence - -ffast-math is unsafe.

千纸鹤 2024-09-23 14:30:15

isnan() 预计会对 -ffast-math 产生未定义的行为。

这是我在测试套件中使用的:

#if defined __FAST_MATH__
#   undef isnan
#endif
#if !defined isnan
#   define isnan isnan
#   include <stdint.h>
static inline int isnan(float f)
{
    union { float f; uint32_t x; } u = { f };
    return (u.x << 1) > 0xff000000u;
}
#endif

isnan() is expected to have undefined behaviour with -ffast-math.

This is what I use in my test suite:

#if defined __FAST_MATH__
#   undef isnan
#endif
#if !defined isnan
#   define isnan isnan
#   include <stdint.h>
static inline int isnan(float f)
{
    union { float f; uint32_t x; } u = { f };
    return (u.x << 1) > 0xff000000u;
}
#endif
旧时浪漫 2024-09-23 14:30:15

对我来说,这看起来像是您的库的 isnan() 实现中的一个错误。它在 Snow Leopard 上的 gcc 4.2.1 上运行良好。然而,尝试一下这个怎么样?

std::isnan(std::abs(yourNanVariable));

显然,我无法测试它,因为 std::isnan(-NaN) 在我的系统上是 true

编辑:使用-ffast-math,无论-O开关如何,雪豹上的gcc 4.2.1都认为NAN = = NANtrueNAN == -NAN 也是如此。这可能会灾难性地破坏代码。我建议放弃 -ffast-math 或至少在使用和不使用它的构建中测试相同的结果......

This looks like a bug in your library's implementation of isnan() to me. It works fine here on gcc 4.2.1 on Snow Leopard. However, what about trying this?

std::isnan(std::abs(yourNanVariable));

Obviously, I can't test it, since std::isnan(-NaN) is true on my system.

EDIT: With -ffast-math, irrespective of the -O switch, gcc 4.2.1 on Snow Leopard thinks that NAN == NAN is true, as is NAN == -NAN. This could potentially break code catastrophically. I'd advise leaving off -ffast-math or at least testing for identical results in builds using and not using it...

佞臣 2024-09-23 14:30:15

您应该能够使用 C99 isnan() 。

如果在你的实现中它不能正常工作(那是哪一个?),你可以通过reinterpret_casting到long并执行IEEE位魔法来实现你自己的。

There's C99 isnan() which you should be able to use.

If in your implementation it does not work correctly (which one is that?) you can implement your own, by reinterpret_casting to long and doing IEEE bit magic.

两人的回忆 2024-09-23 14:30:15

您可以检查数字的位数。 IEEE 754 定义了 NaN 的掩码:

  • 信令 NaN 由 X'7F80 0001' 和 X'7FBF FFFF' 之间或 X'FF80 0001' 和 X'FFBF FFFF' 之间的任何位模式表示。
  • 安静 NaN 由 X'7FC0 0000' 和 X'7FFF FFFF' 之间或 X'FFC0 0000' 和 X'FFFF FFFF' 之间的任何位模式表示。

这可能不便于移植,但如果您确定自己的平台,这是可以接受的。更多: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm

You can check the bits of number. IEEE 754 has defined mask for NaN:

  • A signaling NaN is represented by any bit pattern between X'7F80 0001' and X'7FBF FFFF' or between X'FF80 0001' and X'FFBF FFFF'.
  • A quiet NaN is represented by any bit pattern between X'7FC0 0000' and X'7FFF FFFF' or between X'FFC0 0000' and X'FFFF FFFF'.

This might be not portable, but if you are sure about your platfofm it can be acceptable. More: http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=/com.ibm.xlf101l.doc/xlfopg/fpieee.htm

吃→可爱长大的 2024-09-23 14:30:15

这是基于评论中发布的维基百科文章。请注意,它完全未经测试 - 它应该让您了解可以做什么。

bool reallyIsNan(float x)
{
    //Assumes sizeof(float) == sizeof(int)
    int intIzedX = *(reinterpret_cast<int *>(&x));
    int clearAllNonNanBits = intIzedX & 0x7F800000;
    return clearAllNonNanBits == 0x7F800000;
}

编辑:我真的认为你应该考虑向 GLibc 人员提交一个 bug。

This is based off the wikipedia article posted in the comments. Note that it's entirely untested -- it should give you an idea of something you can do though.

bool reallyIsNan(float x)
{
    //Assumes sizeof(float) == sizeof(int)
    int intIzedX = *(reinterpret_cast<int *>(&x));
    int clearAllNonNanBits = intIzedX & 0x7F800000;
    return clearAllNonNanBits == 0x7F800000;
}

EDIT: I really think you should consider filing a bug with the GLibc guys on that one though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文