为什么NaN不等于NaN?

时间:2012-04-05 18:43:02

标签: floating-point language-agnostic nan ieee-754

相关的IEEE标准定义了一个数字常量NaN(不是数字),并规定NaN应该比较为不等于它自己。那是为什么?

我熟悉的所有语言都实现了这条规则。但它经常会导致严重的问题,例如当NaN存储在容器中时,NaN存在于正在排序的数据中等时的意外行为等。更不用说,绝大多数程序员都希望任何对象都等于自身(在他们了解NaN之前,令人惊讶的是他们增加了错误和混乱。

IEEE标准经过深思熟虑,因此我确信NaN的比较与其自身相比有一个很好的理由。我只是想不通它是什么。

6 个答案:

答案 0 :(得分:139)

接受的答案是100%,毫无疑问错误。不是错误的一半,甚至是错误的错误。我担心这个问题会在搜索中弹出这个问题的时候长时间混淆和误导程序员。

NaN被设计为通过所有计算传播,像病毒一样感染它们,所以如果你在深度,复杂的计算中的某个地方遇到NaN,你就不会冒出一个看似明智的答案。否则通过身份NaN / NaN应该等于1,以及所有其他后果,如(NaN / NaN)== 1,(NaN * 1)== NaN等。如果你想象你的计算在某处出错了(舍入产生了一个零分母,产生NaN)等,那么你可能会因为你的计算而得到非常不正确(或更糟:精确错误)的结果而没有关于原因的明显指标。

当探测数学函数的值时,NaNs在计算中也有很好的理由;链接文档中给出的一个示例是找到函数f()的零()。完全可能的是,在使用猜测值探测函数的过程中,您将探测函数f()不会产生明显结果的函数。这允许zeros()查看NaN并继续其工作。

NaN的替代方案是在遇到非法操作(也称为信号或陷阱)时立即触发异常。除了您可能遇到的巨大性能损失之外,当时无法保证CPU会在硬件中支持它,或者OS /语言会在软件中支持它;在处理浮点时,每个人都有自己独特的雪花。 IEEE决定在软件中明确地将其作为NaN值处理,因此它可以在任何操作系统或编程语言中移植。正确的浮点算法通常在所有浮点实现中都是正确的,无论是node.js还是COBOL(hah)。

理论上,您不必设置特定的#pragma指令,设置疯狂的编译器标志,捕获正确的异常,或安装特殊的信号处理程序,以使看起来相同的算法实际上正常工作。不幸的是,一些语言设计者和编译器编写者已经非常忙于尽力撤销这个功能。

请阅读有关IEEE 754浮点历史的一些信息。对于委员会成员回答的类似问题,这个答案也是如此:What is the rationale for all comparisons returning false for IEEE754 NaN values?

"An Interview with the Old Man of Floating-Point"

"History of IEEE Floating-Point Format"

What every computer scientist should know about floating point arithmetic

答案 1 :(得分:94)

好吧,log(-1)提供NaNacos(2)也提供NaN。这是否意味着log(-1) == acos(2)?显然不是。因此,NaN与自身不相等是完全合理的。

近两年后重新审视这里,这是一个“NaN安全”的比较功能:

function compare(a,b) {
    return a == b || (isNaN(a) && isNaN(b));
}

答案 2 :(得分:28)

我的原始答案(从4年前开始)批评现代观点的决定而不理解作出决定的背景。因此,它没有回答这个问题。

正确答案为here

  

NaN!= NaN源于两个务实的考虑因素:

     

[...]在8087算术中NaN正式化时,没有isnan( )谓词;有必要为程序员提供一种方便有效的方法来检测NaN值,这些值不依赖于编程语言,提供类似isnan( )的东西可能需要很多年

这种方法有一个缺点:它使NaN在许多与数值计算无关的情况下变得不那么有用。例如,很久以后当人们想要使用NaN来表示缺失值并将它们放在基于散列的容器中时,他们就无法做到。

如果委员会预见到未来的使用案例,并认为它们非常重要,那么他们可能会更加详细!(x<x & x>x)而不是x!=x作为NaN的测试。然而,他们的重点更加务实和狭隘:为数值计算提供最佳解决方案,因此他们认为他们的方法没有问题。

===

原始答案:

我很抱歉,正如我欣赏进入最高投票回答的想法一样,我不同意。 NaN并不意味着“未定义” - 请参阅http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF,第7页(搜索“未定义”一词)。正如该文件所证实的那样,NaN是一个定义明确的概念。

此外,IEEE方法是尽可能遵循常规数学规则,当它们不能时,遵循“最少意外”规则 - 见https://stackoverflow.com/a/1573715/336527。任何数学对象都等于它自己,所以数学规则意味着NaN == NaN应该是真的。我看不出任何有效和有力的理由偏离这样一个重要的数学原理(更不用说比较三分法的不太重要的规则等)。

结果,我的结论如下。

IEEE委员会成员并没有非常清楚地认为这一点,并犯了一个错误。由于很少有人理解IEEE委员会的做法,或者关心NaN的标准究竟是什么(也就是说:大多数编译器对NaN的处理都违反了IEEE标准),没有人发出警报。因此,这个错误现在已经嵌入到标准中。它不太可能被修复,因为这样的修复会破坏很多现有的代码。

编辑:Here is one post来自一个非常翔实的讨论。注意:要获得无偏见的视图,您必须阅读整个线程,因为Guido对其他一些核心开发人员的看法不同。然而,Guido对这个话题并不感兴趣,并且很大程度上遵循了Tim Peters的建议。如果任何人有蒂姆·彼得斯的论据支持NaN != NaN,请在评论中添加;他们很有可能改变我的观点。

答案 3 :(得分:7)

一个不错的属性是:如果x == x返回false,则xNaN.

(可以使用此属性检查x是否为NaN。)

答案 4 :(得分:6)

试试这个:

var a = 'asdf';
var b = null;

var intA = parseInt(a);
var intB = parseInt(b);

console.log(intA); //logs NaN
console.log(intB); //logs NaN
console.log(intA==intB);// logs false

如果intA == intB为真,那可能会导致你得出结论a == b,但显然不是。

另一种看待它的方法是,NaN只会向您提供有关什么不是什么,而不是它是什么的信息。例如,如果我说'苹果不是大猩猩'和'橙子不是大猩猩',你会得出'苹果'='橙色'的结论吗?

答案 5 :(得分:2)

实际上,数学中有一个被称为“统一”价值的概念。这些值是经过精心构建的扩展,用于协调系统中的外围问题。例如,您可以将复杂平面中的无限环视为一个点或一组点,而一些以前自命不凡的问题就会消失。关于集合的基数,还有其他一些例子,你可以证明你可以选择无穷大连续体的结构,只要| P(A)| &GT; | A |什么都没有打破。

免责声明:在数学学习期间,我只是模仿我对一些有趣的警告的模糊记忆。如果我在表达上面提到的概念方面表现糟糕,我道歉。

如果你想相信NaN是一个孤独的值,那么你可能会对一些结果感到不满,比如等式运算符没有按照你期望/想要的方式工作。但是,如果您选择相信NaN更像是一个单独占位符所代表的“坏”的连续体,那么您对等于运算符的行为非常满意。换句话说,你看不到你在海里捕获的鱼,但是你会看到另一个看起来相同的鱼,但就像臭一样。

相关问题