为啥 IEEE-754 决定 NaN != NaN 尽管不合逻辑?

Posted

技术标签:

【中文标题】为啥 IEEE-754 决定 NaN != NaN 尽管不合逻辑?【英文标题】:Why did IEEE-754 decide NaN != NaN despite being illogical?为什么 IEEE-754 决定 NaN != NaN 尽管不合逻辑? 【发布时间】:2016-10-12 11:48:05 【问题描述】:

这是What is the rationale for all comparisons returning false for IEEE754 NaN values? 的后续问题(我认为这是另一个问题而不是评论)。它有一个很好的答案,但缺少一件重要的事情:为什么是NaN != NaN

我知道 NaN 与数字相比是无序的,因此 NaN < xNaN > x 总是错误的。但这并不能解释为什么平等是倒退的。引用我链接到的问题的答案:

有必要为程序员提供一种方便有效的检测 NaN 值的方法,这种方法不依赖于提供诸如 isNan() 之类的编程语言

那好吧:

if((0/0) == x) print "I found NaN!";
//using 0/0 is more readable than using 0x7fc00000 or 0x7ff8000000000000

这很方便、高效,并且不依赖于编程语言提供的结构。即使编程语言没有定义全局 NaN,您也可以轻松地自己创建一个局部(或全局)变量。

一个额外的操作肯定不是低效的,特别是因为 FPU 有一个硬编码的响应(不需要计算)。使用 0/0 也比不合逻辑的x!=x 更方便程序员。

程序很少需要检查 NaN 并且每次检查都有一个额外的操作不会成为程序效率低下的原因。同样,没有任何程序如此受限制,以至于它不能处理一个额外的变量,尤其是临时变量。

对我来说,“我需要NaN!=NaN 以便我可以检测到它”的论点没有意义:它与“我需要Infinity!=Infinity 以便我可以检测它”的论点完全相同。不,你没有,只需像其他人一样将其与 1/0 进行比较。

所以我的问题比原来的问题要窄得多:为什么是NaN != NaN

我不知道以前是否有人问过这个问题,因为原来的问题有很多重复。

旁注:

NaN == NaN 现在不会改变

虽然 FPU 现在不会改变,但一些编程语言已经改变,所以 NaN == NaN 是真的。

编辑:

让我试着澄清一下为什么到目前为止我还没有接受任何答案。到目前为止,我已经阅读了 cmets,很抱歉我丢了球:我没有解释为什么我不同意,而是问“为什么”,希望你能给出不同的理由。所以让我试着更好地解释我的观点。

Infinity 和 NaN 在很多方面都很相似。

    Infinity 和 NaN 都是概念。无限不是一个数字,它是一个永无止境的概念。如果 x 是 Infinity,则它表示 x 是无限的。 NaN 不是数字(duh),表示无效状态或数学上无效的东西。对于数学完整性:无穷大是debatably a "number" 但是浮点数近似实数(尽管它们更接近于有理数)并且无穷大肯定不在实数中。因此,就 FPU 和实数线而言,无穷大(所有类型)不是数字(根据域对“数字”的定义)。 相等并不总是数字相等。如果两个操作数都是数字(不是概念),即使要求 FPU 执行比较,一般相等仅是数字相等。当一个操作数是 Infinity 或 NaN 时,使用一般相等来查看另一个是否代表相同的概念。例如x == Infinity 不使用数值相等(因为 Infinity 不是数字)而是检查 x 是否代表正无穷大的概念。如果 x 是“非数字”的概念,我同样希望 x == NaN 会返回完全相同的东西,但事实并非如此(至于为什么这个问题的重点)。 两者都可以通过除以 0 来检索:0/0 返回 NaN,1/0 返回 Infinity。如果您的语言没有 Infinity 的常数,那么 x == (1/0) 是将 x 与 Infinity 进行比较的方法。 多种口味。 Infinity 有无数种类型(因为基数)。然而,FPU 没有任何区别:Infinity 是通用的,也是唯一可用的。例如,使用这些构造,不可能询问 x 是否是可数无穷大。 NaN 在概念上只有一种类型(“不是数字”),但它有两种处理方式:安静和信号。我对信号一无所知,但根据我的理解,如果您尝试比较它会抛出的信号 NaN 的相等性,因此由于它不具有相等性,因此并不完全是主题。 位表示。 NaN 有许多不同的位表示,但 Infinity 只有 1(和 1 表示 -Infinity)。然而,NaN 的所有表示在逻辑上都是完全相同的值,因为它们都表示“非数字”的相同概念。 0x7FF8000000000000 和 0x7FF8000000000001 之间没有区别:它们的含义完全相同,并且允许返回完全相同的数学结果。你不能问 x 是 sqrt(-1) 还是 x 是 log(-1),因为这两者都会返回完全相同的东西:NaN。类似于只有一种 Infinity,只有一种(安静的)NaN。是的,有多个位表示,但一个不大于其他:FPU 使用特殊逻辑将它们完全一样对待。这同样适用于 -0,它的位与 +0 不同,但处理方式完全相同。因此,这些位是与逻辑相等无关的实现细节。 (离题,但它们也都有特殊的数学运算:x-1 不会改变 Infinity 或 NaN 的值)。

所以是的,当你说“平等没有意义,因为它不是一个数字”时,我确实听到了你的声音,但我不同意这种说法,因为如果它是真的,那么按照 #1 它也没有意义比较 Infinity(也不是数字)。尽管按照#2,比较非数字确实有意义。

事实上,我已经阅读了原始问题的所有答案(以及Why are floating point infinities, unlike NaNs, equal?,其中有一些相关的答案)。为什么NaN != NaN 是真的所有论点都归结为“因为它不是一个数字”(已经解决)或“因为有许多不同的位表示”,这被#5 反驳了。

我想不出任何逻辑基础来解释为什么 NaN 应该具有不同的相等性。我并不是说我已经解释了一切:那么我错过了什么?我的一个论点是错误的还是我没有想到的其他原因?这就是为什么我一直在使用“为什么”这个词。如果您不同意,请反驳我或捍卫您的观点。我猜我的上述逻辑至少会有 1 个反驳点,我期待听到它。

再次抱歉,由于我一直在考虑它们,因此我没有将这些包含在原始问题中。我也很抱歉,这可能会对现有答案造成很大的改变。

【问题讨论】:

不只有一个 NaN。由于 NaN 不是数字,因此您不能将其与任何东西进行比较,甚至不能与另一个 NaN 进行比较。 @tim18:“积极优化”意味着编译器损坏?当x 是浮点数时,您无法消除对x != x 的测试,除非您证明x 不是NaN。 无论如何,0/0(整数除法)在许多编程语言中都不会产生 NaN。 @tim18 我不明白你的意思。如果启用-ffast-mathisnan 也会被破坏:godbolt.org/g/Jt1LMt @SkySpiral7 我根本没有这么说。我说过测试一个数字是否为 NaN 的能力是一个目标,这两个约定都满足,但是除了优雅地表达许多算法之外,允许每个浮点值 x 都有 NaN != x,这是一个客观反映通过整个标准。 【参考方案1】:

有几种方法可以产生NaN

想象一下:

double expr1 = 0.0 / 0.0;
double expr2 = Math.log(-1.0);
if (expr1 == expr2) 
  // They are the same

这两个NaNs,如果它们可以表示为数字,很可能是不同的值。

如果NaN == NaN 成立,这个sn-p 会产生意想不到的结果,即比较它们是否相等。由于大多数程序都会根据他们的期望进行比较(所以他们使用==<=,而不是!==!<=),返回false不会使这些程序得出错误的结论。

【讨论】:

(假设它应该是 expr1 = 0.0 / 0.0)。它们都产生 NaN(即相同),因此期望 expr1 == expr2 返回 true。 (另一方面,Infinity == NaN 永远没有意义)。 感谢您的注意,我在书面论证中遗漏了一句话。已添加。 @SkySpiral7:不,它们相同。 NaN 有几种不同的按位表示,并且它们不相同,因此即使您比较它们的位表示,它们(必然)也不相同。所以NaN == NaN 不成立,不是根据定义,也不是根据它们的按位表示。 没有一个单一的 NaN 值。有一系列位组合都被视为 NaN。 我觉得这个论点没有说服力:1.0 / 0.0 == Math.exp(1000.0) 是真的同样出乎意料,IMO。然而它在正常的 IEEE 754 算法中是正确的。 对不起,但我希望他们是平等的。请参阅编辑(特别是第 2 点)。

以上是关于为啥 IEEE-754 决定 NaN != NaN 尽管不合逻辑?的主要内容,如果未能解决你的问题,请参考以下文章

IEEE754 浮点数系统中的 NaN 和 +-INF

为啥无穷大 × 0 = NaN?

NAN传播和IEEE 754标准

IEEE754 中 inf==inf 的基本原理是啥

java中Infinity(无限)和NaN

0.0 / 0.0是一个定义明确的值吗?