【问题标题】:Negative NaN is not a NaN?负 NaN 不是 NaN?
【发布时间】:2011-04-05 12:39:40
【问题描述】:

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

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

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

打印x 的值后,结果发现它是负NaN (-nan),这在我的情况下是完全可以接受的。

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

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

如何检查 NaN -NaN 的相等性?

【问题讨论】:

  • o_O?负数 NaN NaN。
  • 您能否展示您如何编写自己的isNaN,或者如果您有编译器中的内置函数?测试 a NaN 的一种方法(如您所见,有几种)是测试 en.wikipedia.org/wiki/NaN 的位模式(指数为 11..11)。
  • @KennyTM 来自 Wikipedia:“因为在实践中,编码的 NaN 既有符号,也有可选的‘诊断信息’(有时称为有效负载),这些通常也可以在 NaN 的字符串表示中找到,例如:-NaN"。有一个 NaN 或很多 NaN,具体取决于您如何看待它,这个编译器打印它的方式可能比有用更令人困惑。
  • 对我来说,这听起来像是您的标准库的 std::isnan 实现中的一个错误。
  • 考虑到我已经听说过的一些恐怖故事,即使在 glibc 中我也不觉得有这样的错误的想法令人惊讶。这是善意的,不要误会我的意思,但编译器作者长期以来一直犯错浮点数。

标签: c++ nan fast-math


【解决方案1】:

你应该可以使用 C99 isnan()。

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

【讨论】:

    【解决方案2】:

    这是基于在 cmets 中发布的维基百科文章。请注意,它完全未经测试——它应该让您知道您可以做些什么。

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

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

    【讨论】:

    • 这也会为Inf返回true
    • @Sam:正如我所说,我没有测试它,我从维基百科得到它。我从来没有声称它的正确性。无论如何,OP已经回答了这个问题。我不明白为什么我们要挖掘一个 1.5 年前的问题,该问题已经被标记为已回答并抱怨它。
    • 我没有抱怨;你是。我从谷歌搜索中找到了这个问题,发现它没有令人满意的答案。我正在评论不正确或不完整的答案,因为我不希望其他人花时间找出他们不正确或不完整的原因。此外,批准的答案给出了解释,而不是解决方案。这个问题有多老绝对没有相关性。如果您对此有疑问,您可能错过了该网站的重点。放松点,伙计。
    • 哦,顺便说一句,我也认为负面的 cmets 比不加评论的投票要文明一个数量级。
    • 接受的答案没有详细说明任何内容,也没有为问题提供解决方法。并且请否决,因为我对同一个问题给出了相同的答案?欢迎来到我的负面好斗人士名单,在他们长大之前,我不再打算与之互动。
    【解决方案3】:

    您可以检查数字的位。 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

    【讨论】:

    • 那将是我最后的选择,我仍在努力寻找最便携的解决方案。
    • @LiraNuna 这不是那么戏剧化。如果你实现它时牢记字节序,它将足够便携
    【解决方案4】:

    这很尴尬。

    编译器(在本例中为 GCC)优化比较并且isnan 返回false 的原因是因为我团队中有人打开了-ffast-math

    来自文档:

    -ffast-数学 设置 -fno-math-errno、-funsafe-math-optimizations、 -fno-trapping-math、-ffinite-math-only、-fno-rounding-math、-fno-signaling-nans 和 fcx-limited-range。 此选项导致定义预处理器宏 __FAST_MATH__。 任何 -O 选项都不应打开此选项,因为它可能导致程序输出不正确,这些程序依赖于数学函数的 IEEE 或 ISO 规则/规范的精确实现。

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

    【讨论】:

    • 我刚刚注意到 MS 编译器与 /fp:fast 标志一起使用时存在完全相同的问题。可与 /fp:precise 配合使用。
    【解决方案5】:

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

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

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

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

    【讨论】:

      【解决方案6】:

      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
      

      【讨论】:

        猜你喜欢
        • 2019-03-26
        • 2011-10-22
        • 2013-12-17
        • 2014-12-14
        • 1970-01-01
        • 2019-04-12
        • 2016-03-19
        • 1970-01-01
        • 2012-09-03
        相关资源
        最近更新 更多