对于IEEE754 NaN值,所有比较返回false的理由是什么?

时间:2009-10-14 09:19:17

标签: floating-point comparison nan ieee-754 iec10967

为什么NaN值的比较与所有其他值的行为不同? 也就是说,所有与运营商的比较==,< =,> =,<,>其中一个或两个值为NaN返回false,与所有其他值的行为相反。

我认为这在某种程度上简化了数值计算,但我找不到明确说明的理由,甚至在Kahan的Lecture Notes on the Status of IEEE 754中也没有详细讨论其他设计决策。

这种异常行为在进行简单的数据处理时会造成麻烦。例如,在对w.r.t记录列表进行排序时。 C程序中的一些实值字段我需要编写额外的代码来处理NaN作为最大元素,否则排序算法会变得混乱。

修改 到目前为止,答案都认为比较NaNs是毫无意义的。

我同意,但这并不意味着正确答案是错误的, 相反,它将是一个非布尔值(NaB),幸运的是它不存在。

因此,在我看来,选择返回true或false进行比较是任意的, 对于一般数据处理,如果它遵守通常的法律将是有利的 (==的反身性,<,==,>的三分法), 以免依赖这些法律的数据结构变得混乱。

所以我要求打破这些法律的一些具体优势,而不仅仅是哲学推理。

编辑2: 我想我现在明白为什么使NaN最大化会是一个坏主意,它会搞砸上限的计算。

NaN!= NaN可能是理想的,以避免检测循环中的收敛,例如

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}
然而,最好通过比较绝对差异和小限制来编写

。 所以恕我直言,这对于打破NaN的自反性是一个相对较弱的论据。

13 个答案:

答案 0 :(得分:461)

我是IEEE-754委员会的成员,我会尽力帮助澄清一些事情。

首先,浮点数不是实数,浮点运算不满足实数算术的公理。三分法并不是真正算术的唯一属性,它不适用于浮点数,甚至也不是最重要的。例如:

  • 添加不是关联的。
  • 分配法不成立。
  • 有没有反转的浮点数。

我可以继续。不可能指定一个固定大小的算术类型来满足我们所熟知和喜爱的实际算术属性的所有。 754委员会必须决定弯曲或打破其中一些。这是由一些非常简单的原则指导的:

  1. 当我们可以时,我们匹配实际算术的行为。
  2. 如果我们不能,我们会尽量使违规行为变得可预测并且易于诊断。
  3. 关于你的评论“这并不意味着正确答案是错误的”,这是错误的。谓词(y < x)询问y是否小于x。如果y是NaN,则小于任何浮点值x,因此答案必定为false。

    我提到三分法不适用于浮点值。但是,有一个类似的属性确实成立。 754-2008标准第5.11条第2款:

      

    四种相互排斥的关系是可能的:小于,等于,大于和无序。当至少一个操作数是NaN时,出现最后一种情况。每个NaN都应该将无序与包括其自身在内的所有东西进行比较。

    就编写处理NaN的额外代码而言,通常可能(尽管并不总是很容易)以一种NaN正确落实的方式构造代码,但情况并非总是如此。如果不是,可能需要一些额外的代码,但代价闭包为浮点运算带来的便利是一个很小的代价。


    附录: 许多评论者认为,保持平等和三分法的反身性更为有用,因为采用NaN!= NaN似乎并没有保留任何熟悉的公理。我承认对这个观点有一些同情,所以我想我会重新审视这个答案,并提供更多的背景。

    我与Kahan谈话的理解是,NaN!= NaN起源于两个实用的考虑因素:

    • x == y应尽可能等同于x - y == 0(除了作为实数算术的定理之外,这使得比较的硬件实现更节省空间,这对于标准制定的时间 - 请注意,这是违反了x = y =无穷大,因此它本身并不是一个很好的理由;它可以合理地弯曲到(x - y == 0) or (x and y are both NaN))。

      < / LI>
    • 更重要的是,当NaN在8087算术中形式化时,没有isnan( )谓词;有必要为程序员提供一种方便有效的检测NaN值的方法,这些方法不依赖于编程语言,提供类似isnan( )的东西可能需要很多年。我将引用卡汉自己关于这个主题的文章:

      

    如果没有办法摆脱NaNs,它们就像CRAYs上的Indefinites一样无用;一旦遇到一个人,计算将最好停止,而不是无限期地持续到无限期结束。这就是为什么NaN上的某些操作必须提供非NaN结果的原因。哪个操作? ......例外是C谓词“x == x”和“x!= x”,对于每个无限或有限数x,它们分别为1和0,但如果x不是数字(NaN)则反向;这些提供了NaNs和缺少NaN单词和谓词IsNaN(x)的语言中数字之间唯一的简单区分。

    请注意,这也是排除返回“Not-A-Boolean”之类的逻辑。也许这种实用主义是错误的,标准应该需要isnan( ),但这将使NaN几乎不可能在世界等待编程语言采用的同时高效便捷地使用几年。我不相信那是一次合理的权衡。

    直言不讳:NaN == NaN的结果现在不会改变。更好地学会忍受它而不是在互联网上抱怨。如果你想争论适用于容器的订单关系,我建议你提倡你最喜欢的编程语言实现IEEE-754(2008)中标准化的totalOrder谓词。事实上,它尚未谈及Kahan关注的有效性,这种关注促成了当前的事态。

答案 1 :(得分:47)

NaN可以被认为是未定义的状态/数字。类似于0/0未定义或sqrt(-3)的概念(在浮点所在的实数系统中)。

NaN用作此未定义状态的一种占位符。从数学上讲,undefined不等于undefined。你也不能说未定义的值大于或小于另一个未定义的值。因此,所有比较都会返回错误。

在将sqrt(-3)与sqrt(-2)进行比较的情况下,此行为也很有用。他们都会返回NaN,但即使他们返回相同的值,他们也不是等价的。因此,在处理NaN时,等式总是返回false是理想的行为。

答案 2 :(得分:32)

再说一个类比。如果我递给你两个盒子,告诉你它们都没有苹果,你会告诉我盒子里面有同样的东西吗?

NaN不包含有关什么是什么的信息,而不包含什么。因此,这些元素绝对不能说是平等的。

答案 3 :(得分:12)

NaN上的维基百科文章中,以下做法可能会导致NaN:

  • 所有数学运算&gt;使用NaN作为至少一个操作数
  • 划分0/0,∞/∞,∞/-∞,-∞/∞和-∞/-∞
  • 乘法0×∞和0×-∞
  • 加法∞+( - ∞),( - ∞)+∞和等效减法。
  • 将函数应用于其域外的参数,包括取负数的平方根,取负数的对数,取90度(或π/ 2弧度)的奇数倍的正切,或取数字的反正弦或余弦小于-1或大于+1。

由于无法知道这些操作中的哪一个创建了NaN,因此没有办法比较那些有意义的操作。

答案 4 :(得分:4)

我不知道设计原理,但这里摘录自IEEE 754-1985标准:

“即使操作数的格式不同,也应该可以比较所有支持格式的浮点数。比较是精确的,永远不会溢出或下溢。四种相互排斥的关系是可能的:小于,等于,大于当至少有一个操作数是NaN时,最后一种情况就出现了。每个NaN都会将无序与包括它自身在内的所有东西进行比较。“

答案 5 :(得分:2)

它看起来很奇怪,因为大多数允许NaN的编程环境也不允许使用3值逻辑。如果你将3值逻辑抛入混合中,它就会变得一致:

  • (2.7 == 2.7)= true
  • (2.7 == 2.6)= false
  • (2.7 == NaN)=未知
  • (NaN == NaN)=未知

即使.NET不提供bool? operator==(double v1, double v2)运算符,您仍然会遇到愚蠢的(NaN == NaN) = false结果。

答案 6 :(得分:1)

我猜NaN(非数字)意味着:这不是一个数字,因此比较它并没有多大意义。

这有点像使用null操作数的SQL中的算术:它们都导致null

浮点数的比较比较数值。因此,它们不能用于非数值。因此,NaN无法在数字意义上进行比较。

答案 7 :(得分:1)

过度简化的答案是NaN没有数字值,因此没有任何内容可以与其他任何内容进行比较。

如果您希望它们像+ INF一样行动,您可以考虑使用+ INF测试和替换您的NaN。

答案 8 :(得分:0)

虽然我同意将NaN与任何实数进行比较应该是无序的,但我认为将NaN与其自身进行比较是有正当理由的。例如,人们如何发现信号NaNs和安静的NaNs之间的区别?如果我们将信号视为一组布尔值(即位向量),则可能很好地询问位向量是相同还是不同并相应地对这些组进行排序。例如,在解码最大偏差指数时,如果有效位被左移,以便在二进制格式的最高有效位上对齐有效数字的最高有效位,则负值将是安静的NaN,任何正值都将是信号NaN。零当然是为无穷大保留的,比较将是无序的。 MSB对齐将允许甚至来自不同二进制格式的信号的直接比较。因此,具有相同信号集的两个NaN将是等价的,并赋予相等的意义。

答案 9 :(得分:0)

NaN是一个隐式的新实例(一种特殊的运行时错误)。这意味着<?php if(isset($_POST['filevoyage'])) { $voyage = $_POST['filevoyage']; $dir = scandir("backup/"); unset($dir[0], $dir[1]); if(count($dir) > 0) { $fileFound = false; foreach($dir as $file) { if((preg_match("/\b$voyage\b/", $file) === 1)) { $finalLink = 'backup/'.$file; echo $finalLink; // I think the problem is in this loop $fileFound = true; } } if(!$fileFound) die("File $voyage doesn't exist"); } else { echo "No files in backup folder"; } } ?> 的原因与NaN !== NaN相同;

请记住,这种隐含性也可以在外部错误中看到,例如在正则表达式的上下文中,它意味着new Error !== new Error,它只是/a/ !== /a/的语法糖

答案 10 :(得分:-2)

因为数学是数字“只存在”的领域。 在计算中,您必须初始化这些数字,并根据您的需要保持状态。 在那些过去,内存初始化以您永远不会依赖的方式工作。你永远不会让自己想到这个“哦,这将一直用0xCD初始化,我的算法不会破坏”

所以你需要适当的非混合溶剂足够粘性,以免你的算法被吸进和破坏。 涉及数字的好算法主要用于关系,而 if()关系将被省略。

这只是你可以在创建时加入新变量的油脂,而不是从计算机内存编程随机地狱。无论你的算法是什么,都不会破坏。

接下来,当您仍然突然发现您的算法正在生成NaN时,可以将其清除,一次查看每个分支。同样,“永远错误”的规则对此有很大的帮助。

答案 11 :(得分:-2)

对我来说,最简单的解释方法是:

  

我有东西,如果它不是苹果,那么它是橙色吗?

你无法将NaN与其他东西(甚至是其自身)进行比较,因为它没有值。它也可以是任何值(数字除外)。

  

我有一些东西,如果它不等于一个数字,那么它是一个字符串吗?

答案 12 :(得分:-2)

非常简短的回答:

因为以下内容: nan / nan = 1 一定不要抱。否则inf/inf将为1。

(因此nan不能等于nan。至于><,如果nan会尊重一套令人满意的订单关系阿基米德的财产,我们将再次nan / nan = 1极限。)