我熟悉的所有语言都实现了这个规则。但是,它经常会引起重大问题,例如当NaN存储在容器中时出现意外行为,当NaN在正在排序的数据中时等等。更不用说,绝大多数程序员期望任何对象都等于自己(在他们了解NaN之前),因此让他们感到惊讶会增加错误和混乱。
IEEE标准经过深思熟虑,所以我确信有一个很好的理由,为什么将NaN与自身比较会出错。我只是想不出来是什么原因。
编辑:请参考此答案作为权威答案。
NaN被设计为通过所有计算来传播,像病毒一样感染它们。因此,如果在您深入复杂的计算中碰到NaN,您就不会得出一个看似合理的答案。否则,按照等同性原则,NaN/NaN应该等于1,以及所有其他后果,例如(NaN/ NaN)==1,(NaN*1)==NaN等。如果您想象一下您的计算在某个地方出错了(舍入产生了零分母,导致NaN),等等,则您可能会从您的计算中获得极其不正确(或更糟糕的是:微妙不正确)的结果,而没有明显的指示器说明为什么。
在探测数学函数的值时,还有很多很好的原因使NaN出现在计算中;链接文档中给出的一个例子是找到函数f()的零点值。在使用猜测值对函数进行探测的过程中,完全有可能会探测到一个函数f()没有合理结果的值。这允许zeros()看到NaN并继续它的工作。
NaN的替代方案是在遇到非法操作时触发异常(也称为信号或陷阱)。除了可能遇到的巨大性能惩罚之外,在当时没有保证CPU会在硬件上支持它,或者OS/语言会在软件上支持它;每个人都有自己独特的方法来处理浮点数。IEEE决定明确地将其作为NaN值在软件中处理,以便跨任何OS或编程语言进行移植。正确的浮点运算算法通常在所有浮点实现中都是正确的,无论是node.js还是COBOL(hah)。
理论上,您不必设置特定的#pragma指令、设置疯狂的编译器标志、捕获正确的异常或安装特殊的信号处理程序,就可以使看似相同的算法实际上正确地工作。不幸的是,一些语言设计师和编译器编写者一直在尽最大努力撤销这个功能。
请阅读一些IEEE 754浮点数的历史信息。此外,请参考类似问题的答案,其中委员会成员回答了:什么是所有比较结果都返回IEEE754 NaN值为假的理由? “浮点运算老人的采访” “IEEE浮点格式的历史” 每一个计算机科学家应该了解的浮点算术知识NaN + 1 != 0
或者 NaN * 1 > 0
的时候,它会返回 True
或者 False
,就好像一切都很正常一样。因此,如果想要使用比较运算符,就不能指望 NaN
保护你免受问题的侵扰。既然比较无法帮助您"传播" NaN,为什么不至少让它们变得有意义呢?目前情况下,它们会破坏 NaN 在字典中的使用案例,使排序变得不稳定,等等。另外,你的回答中还有一个小错误。如果按照我的方式,NaN/NaN == 1
不会评估为 True
。 - max许多评论者认为保留相等关系的自反性和三歧性更有用,理由是采用NaN!= NaN似乎并没有保留任何熟悉的公理。我承认我对这个观点有些同情,所以我想重新审视一下这个答案,并提供更多背景信息。
因此,尊敬的先生,也许你可以在陈述时稍微缓和一下语气。 - maxisnan()
函数的情况是一个合理的原因之外,我在这个回答中找不到任何说明NaN != NaN的理由。然而,我看不出今天仍然有效的任何理由,除了更改语义将是一个非常糟糕的想法。 - Sven Marnach好的,log(-1)
会返回NaN
,acos(2)
也会返回NaN
。这是否意味着log(-1) == acos(2)
?显然不是这样。因此,NaN
不等于本身是完全有道理的。
两年后重新审视这个问题,这里有一个“NaN安全”的比较函数:
function compare(a,b) {
return a == b || (isNaN(a) && isNaN(b));
}
log
函数和acos
函数的交点,那么所有小于-1的负值都被认为是交点。有趣的是,在实际数学中不能这样说,但是 Infinity == Infinity
为真。 - Niet the Dark Absol我的原始答案(来自4年前)在不了解做出决定的背景情况下,从现代角度批评了该决定。因此,它没有回答问题。
正确的答案在这里给出:
NaN
!=NaN
源于两个实用考虑:[...]在8087算术中正式确定NaN时,没有
isnan()
谓词,需要为程序员提供一种方便高效的方法来检测不依赖于编程语言提供类似isnan()
的NaN值,这可能需要很多年时间
这种方法有一个缺点:它使NaN在许多与数值计算无关的情况下变得不太有用。例如,当人们想要使用NaN
表示缺失值并将其放入基于哈希的容器中时,他们无法这样做。
!(x<x & x>x)
代替 x!=x
作为测试 NaN
的方法。然而,他们的关注点更加实用和狭窄:为数值计算提供最佳解决方案,因此他们认为自己的方法没有问题。
===
原始回答:
很抱歉,虽然我很欣赏最高票答案的思路,但我不同意它。NaN并不意味着“未定义”-请参见http://www.cs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF第7页(搜索“undefined”一词)。正如该文档所确认的那样,NaN是一个明确定义的概念。
此外,IEEE的方法是尽可能遵循常规数学规则,当他们无法遵循时,遵循“最小惊讶原则”-请参见https://dev59.com/rnI_5IYBdhLWcg3wAeLl#1573715。任何数学对象都等于自身,因此数学规则会暗示NaN == NaN应该为True。我看不到任何有效而有力的理由偏离这样一个重要的数学原则(更不用说比较三分律等不太重要的规则了)。
因此,我的结论如下。
IEEE委员会成员没有仔细思考,犯了一个错误。由于很少有人理解IEEE委员会的方法,或者关心标准对NaN的确切规定(即:大多数编译器对NaN的处理方式都违反了IEEE标准),因此没有人发出警报。因此,这个错误现在已经嵌入到标准中。由于这样的修复将破坏许多现有的代码,所以不太可能被修复。编辑:这里是一个帖子,来自一个非常有见地的讨论。注意:要获得公正的观点,您必须阅读整个主题,因为Guido与其他核心开发人员持不同的观点。然而,Guido对这个话题不太感兴趣,并且主要遵循Tim Peters的建议。如果有人拥有Tim Peters支持NaN != NaN
的论据,请在评论中添加它们;它们有很大机会改变我的观点。NaN
违反三分律是有道理的,但和你一样,我看不到不定义等价关系时使用 ==
的合理语义理由(更进一步,如果这样的比较不能实现等价关系,我认为编程语言应明确禁止对不同类型的事物进行比较,即使存在隐式转换)。等价关系的概念在编程和数学中如此基础,违反它似乎很荒谬。 - supercatNaN==NaN
返回除true或false之外的其他结果将会有问题。但是,由于(a<b)
不一定等于!(a>=b)
,所以我认为(a==b)
不必一定等于!(a!=b)
。如果让NaN==NaN
和Nan!=NaN
都返回false,则可以使需要任一定义的代码使用所需的定义。 - supercat一个很好的特性是:如果x == x
返回false,则x
是 NaN
。
(可以使用这个特性来检查 x
是否为 NaN
。)
a
和 b
之间等价关系的代码可以使用 !(a != b)
。 - supercat试试这个:
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
a=16777216f
,b=0.25
,c=0.125
,那么 a+b == a+c
这个事实是否意味着 b==c
?还是仅仅意味着这两个计算结果是无法区分的?如果没有区分它们的方法,为什么不能认为sqrt(-1)和(0.0/0.0)是无法区分的呢? - supercat==
的自反性。 - Alexander