负 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 != NaN
和assert(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 == NAN
是true
,NAN == -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?的主要内容,如果未能解决你的问题,请参考以下文章