负 NaN 不是 NaN?

Posted

技术标签:

【中文标题】负 NaN 不是 NaN?【英文标题】:Negative NaN is not a 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 中我也不觉得有这样的错误的想法令人惊讶。这是善意的,不要误会我的意思,但编译器作者长期以来一直犯错浮点数。 【参考方案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

【讨论】:

以上是关于负 NaN 不是 NaN?的主要内容,如果未能解决你的问题,请参考以下文章

numpy nan和inf

正面与负面的nans

C++中非数nan的定义与范例

如何在 Perl 中创建或测试 NaN 或无穷大?

NaN 是不是等于 NaN?

NaN