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


267

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

我想这以某种方式简化了数值计算,但是我找不到明确陈述的理由,甚至在Kahan撰写的关于IEEE 754状态讲义中也没有详细讨论其他设计决策。

在进行简单的数据处理时,这种异常行为会引起麻烦。例如,当在C程序中对带有某些实值字段的记录列表进行排序时,我需要编写额外的代码以将NaN作为最大元素来处理,否则排序算法可能会变得混乱。

编辑: 到目前为止,所有答案都表明,比较NaN毫无意义。

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

因此,在我看来,选择比较返回真还是假是任意的,并且对于一般数据处理,如果遵循通常的规律(反射性为==,三分法为<,==,>),则最好避免数据结构,这将是有利的依靠这些法律变得混乱。

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

编辑2: 我想我现在明白为什么将NaN设为最大值会是个坏主意,它将使上限计算变得混乱。

NaN!= NaN可能需要避免检测诸如

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

但是最好通过比较绝对差和较小的限制来写出。因此恕我直言,这对于打破NaN的反射率是一个相对较弱的论点。


2
NaN一旦进入计算,通常将永远不会离开,因此您的收敛测试将成为无限循环。通常最好通过返回NaN来报告失败的收敛到调用例程。因此,while (fabs(x - oldX) > threshold)如果发生收敛或NaN进入计算,则循环结构通常会变成,退出循环。然后,NaN的检测和适当的补救措施将在循环之外发生。
斯蒂芬·佳能

1
如果NaN是顺序的最小元素,则while循环仍然有效。
starblue

Answers:


535

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

首先,浮点数不是实数,并且浮点算术不满足实数算术的公理。三分法不是实数运算的唯一属性,它对浮点数不成立,甚至不是最重要的。例如:

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

我可以继续。不可能指定一个固定大小的算术类型来满足我们所知道和喜欢的实数算术的所有属性。754委员会必须决定弯曲或折断其中的一些。这遵循一些非常简单的原则:

  1. 如果可以,我们匹配实数运算的行为。
  2. 当我们无法做到时,我们会尝试使违规行为可预测且易于诊断。

关于您的评论“并不意味着正确答案是错误的”,这是错误的。谓词(y < x)询问是否y小于x。如果y为NaN,则它小于任何浮点值x,因此答案必然为假。

我提到三分法不适用于浮点值。但是,确实存在类似的属性。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))。

  • 更重要的isnan( )是,在8087算术中没有正式使用NaN的谓词。有必要为程序员提供一种便捷有效的方法来检测NaN值,而这些值不依赖于编程语言,而NaN值isnan( )可能需要花费很多年。我将引用卡汉(Kahan)在该主题上的著作:

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

请注意,这也是排除返回“ Not-A-Boolean”之类的逻辑的逻辑。也许这种实用主义是放错了地方,而该标准应该是必需的isnan( ),但这将使NaN在全世界等待编程语言采用的几年中几乎无法高效,方便地使用。我不认为这是一个合理的权衡。

直言不讳:NaN == NaN的结果现在不会改变。学会与之相处比与在网络上抱怨更好。如果您想主张应该存在适合于容器的顺序关系,我建议提倡您最喜欢的编程语言实现totalOrderIEEE-754(2008)中标准化的谓词。事实尚未证明Kahan引起当前状况的担忧的有效性。


16
我读了您的第1点和第2点。然后,我观察到在实际算术中(首先是允许NaN扩展),NaN等于自身-仅仅是因为在数学中,任何实体都毫无例外地等于自身。现在我很困惑:为什么IEEE不能“匹配实数算术的行为”,这会使NaN == NaN?我想念什么?
最大

12
同意;NaN的非自反性以其基于相等的包含语义为Python之类的语言带来了无尽的痛苦。当您试图在其上构建容器时,您真的不希望平等成为等价关系。对于一个应该易于学习的语言,拥有两个独立的平等概念也不是一个友好的选择。结果(对于Python而言)是对IEEE 754的尊重与不太破译的包容语义之间令人不愉快的脆弱折衷。幸运的是,很少将NaN放入容器中。
Mark Dickinson

5
一些不错的观测位置:bertrandmeyer.com/2010/02/06/...
马克·迪金森

6
@StephenCanon:用什么方式(0/0)==(+ INF)+(-INF)会比拥有1f/3f == 10000001f/30000002f什么更荒谬吗?如果将浮点值视为等价类,则a=b并不意味着“产生的ab如果进行无限精确的计算将产生相同的结果”,而是“已知的a与已知的匹配b”。我很好奇,如果您知道任何代码示例,使用“ Nan!= NaN”会使事情比以前更简单吗?
2013年

5
从理论上讲,如果您有NaN == NaN且没有isNaN,则仍然可以使用来测试NaN !(x < 0 || x == 0 || x > 0),但它会比慢,笨拙x != x
user2357112支持Monica's

50

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

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

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


5
sqrt(1.00000000000000022)== sqrt(1.0)的结果应该是什么?(1E308 + 1E308-1E308-1E308-1E308)==(1E308 + 1E308)呢?同样,六个比较中只有五个返回假。该!=运算符返回true。具有NaN==NaNNaN!=NaN都返回false将允许比较x和y的代码通过选择==或来选择当两个操作数均为NaN时应该发生的情况!=
2013年

38

再作一个类比。如果我递给您两个盒子,告诉您它们都不包含一个苹果,您能告诉我这些盒子包含相同的东西吗?

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


6
根据定义,所有空集都是相等的。
MSalters

28
给出的框不为空。
约翰·史密斯,

7
你能告诉我盒子里没有相同的东西吗?我可以理解的理由(NaN==Nan)==false。我不了解的理由(Nan!=Nan)==true
2014年

3
我假设NaN!= NaN是正确的,因为x!= y被定义为!(x == y)。当然,我不知道IEEE规范是否以这种方式定义它。
Kef Schecter 2014年

6
但是以此类推,如果您给我一个盒子,说它不包含苹果,然后问我它是否等于苹果,您希望我说不?因为这就是我根据IEEE所说的话。
分号

12

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

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

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


3
而且,即使您知道哪个操作,也无济于事。我可以构造任意数量的公式,这些公式在某个时刻变为0/0,这些公式具有(如果假设为连续性)定义明确且在该时刻具有不同值。
David Thornley,2009年

4

我不知道设计原理,但以下是IEEE 754-1985标准的摘录:

“即使操作数的格式不同,也应该可以比较所有受支持格式的浮点数。比较是精确的,并且永远不会上溢或下溢。可能存在四个互斥关系:小于,等于,大于和无序最后一种情况发生在至少一个操作数是NaN的情况下。每个NaN都应将无序与所有事物(包括自身)进行比较。”


2

它看起来似乎很特殊,因为大多数允许NaN的编程环境也不允许三值逻辑。如果将三值逻辑放入混合,它将变得一致:

  • (2.7 == 2.7)=真
  • (2.7 == 2.6)=否
  • (2.7 == NaN)=未知
  • (NaN == NaN)=未知

甚至.NET都不提供bool? operator==(double v1, double v2)运算符,因此您仍然会陷入愚蠢的(NaN == NaN) = false结果。


1

我猜想NaN(非数字)的确切含义是:这不是数字,因此比较它实际上没有任何意义。

有点像SQL中带有null操作数的算术运算:它们都产生null

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


3
“这不是一个数字,因此比较它实际上没有任何意义。” 字符串不是数字,但是比较它们是有意义的。
杰森

2
是的,将字符串与字符串进行比较是有意义的。但是将字符串与例如苹果进行比较并没有多大意义。既然苹果和梨不是数字,那么比较它们是否有意义?哪个更大?
达伦·托马斯

@DarenThomas:在SQL中,都不是“ IF NULL = NULL THEN FOO;”。也不是“如果Null <> Null THEN CALL FOO;” [或任何语法是]将执行FOO。要使NaN等效,if (NaN != NaN) foo();不应执行foo,但可以执行。
2013年

1

过于简单的答案是NaN没有数字值,因此其中没有东西可以与其他任何东西进行比较。

如果您希望NaN像+ INF一样工作,则可以考虑测试并用+ INF替换NaN。


0

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


-1

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

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

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

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


您的意思是“除数字外,它可以是任何值”?
pushkin '18

-2

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

因此,您需要适当的非混合溶剂,该溶剂应具有足够的粘性,以免使您的算法陷入困境。好的涉及数字的算法将主要用于处理关系,而那些if()关系将被省略。

这只是油脂,您可以在创建时将其放入新变量中,而不用从计算机内存中编程随机地狱。而且无论您使用哪种算法,都不会中断。

接下来,当您仍然突然发现自己的算法正在产生NaN时,可以清除它,一次查看每个分支。同样,“总是错误的”规则在这方面大有帮助。


-4

答案很简短:

因为以下原因: nan / nan = 1 不能成立。否则inf/inf为1。

(因此nan不能等于nan。至于><,如果nan要尊重满足Archimedean属性的集合中的任何顺序关系,我们将再次nan / nan = 1处于极限)。


2
不,那没有道理。我们有inf = infinf / inf = nan,因此nan = nan也不会阻止nan / nan = nan
starblue

@starblue你的意思是nan / nan = 1?无论如何...如果inf和nan与其他任何数字一样,则您的推理确实有意义。事实并非如此。之所以inf/inf必须是nan(或数学中的不确定形式)而不是1它的原因,要比简单的代数运算(见De L'Hospital定理)更为微妙。
SeF
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.