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

我想这在某种程度上简化了数值计算,但我找不到明确说明的原因,即使在 关于 IEEE 754 现状的讲义 作者:Kahan,详细讨论了其他设计决策。

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

编辑:到目前为止的答案都认为比较 NaN 是没有意义的。

我同意,但这并不意味着正确的答案是错误的,而是一个不是一个树立(NAB),幸运的是不存在。

因此,在我看来,选择对比较的返回对还是错是任意的,对于一般数据处理,如果它遵守通常的法律(反射性,<,==,>),则将是有利的,也可以依靠这些法律变得困惑。

因此,我要求打破这些法律带来一些具体的好处,而不仅仅是哲学推理。

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

NaN != NaN 可能需要避免检测循环中的收敛,例如

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

然而,最好通过将绝对差异与一个小极限进行比较来编写。所以恕我直言,这是打破 NaN 自反性的一个相对较弱的论据。

有帮助吗?

解决方案

我是 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( ) NaN 在 8087 算术中形式化时的谓词;有必要为程序员提供一种方便有效的方法来检测 NaN 值,该方法不依赖于提供类似以下内容的编程语言 isnan( ) 这可能需要很多年的时间。我将引用卡汉自己关于这个主题的文章:

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

请注意,这也是排除返回“非布尔值”之类的内容的逻辑。也许这种实用主义是错误的,标准应该要求 isnan( ), ,但这将使得 NaN 在世界等待编程语言被采用的几年里几乎不可能高效、方便地使用。我不相信这是一个合理的权衡。

直言不讳地说:NaN == NaN 的结果现在不会改变。学会忍受它比在互联网上抱怨更好。如果你想争论适合容器的顺序关系应该 存在,我建议提倡您最喜欢的编程语言实现 totalOrder 谓词在 IEEE-754 (2008) 中标准化。事实上,它还没有说明卡汉的担忧是正确的,而这种担忧正是造成当前事态的原因。

其他提示

的NaN可以被认为是未定义的状态/数。类似的0/0的概念被未定义或SQRT(-3)(在实数系统,其中所述浮点生活)。

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

此行为也是在你比较SQRT(-3)SQRT(-2)的情况下是有利的。他们将都返回NaN的,但他们都没有,即使他们返回相同的值相等。因此具有平等的NaN处理时总是返回false是所期望的行为。

要在又一个比喻抛出。如果我交给你两个箱子,并告诉你,他们既不包含一个苹果,你能告诉我,这些箱子包含同样的事情?

NaN的不包含任何有关是什么东西,它不是什么。因此,这些元件可以从未明确说是相等的。

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

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

由于无法知道哪些操作创建了 NaN,因此无法对它们进行有意义的比较。

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

“这应该是可能的浮点数在所有支持的格式比较,即使操作数的格式不同,比较确凿,永远不会溢出或下溢四个相互排斥的关系是可能的:小于,等于,大于和无序的。当至少一个操作数为NaN最后一种情况发生,每一个的NaN应比较无序的一切,包括其本身。“

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

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

即使 .NET 也不提供 bool? operator==(double v1, double v2) 运算符,所以你仍然陷入愚蠢的困境 (NaN == NaN) = false 结果。

我猜测,NaN(非数字)表示正是:这是不是一个数量,从而比较它并没有真正意义

这是一个有点像算术SQL与null操作数:他们都导致null

浮点数的比较比较数值。因此,它们不能被用于非数字值。为NaN因此不能在数字感进行比较。

在过于简单的回答是,楠没有数值,所以什么也没有要比较的任何东西。

您可能会考虑进行测试,并与+ INF更换您的NaN,如果你想让他们像+ INF行动。

NaN的是隐式的新的实例(一类特殊的运行时错误的)。这意味着对于同样的原因,NaN !== NaN new Error !== new Error;

和记住这样含蓄还可以看出外部误差,例如在正则表达式的上下文中是指/a/ !== /a/其是用于new RegExp('a') !== new RegExp('a')只是语法糖

虽然我同意,任何实数为NaN的比较应该是无序的,我认为有正当理由楠与自身比较。怎么样,例如没有一个发现信号NaN和无提示NaN之间的区别?如果我们认为的信号作为一组布尔值(即一个位向量)一个很可能会问的位向量是否相同或不同的,并相应地命令集。例如,在解码的最大偏置指数,如果有效数被左移以便对准在二进制格式的最显著位有效数的最显著位,负值将静态NaN和任何正值将是一个信令为NaN。当然,零保留为无穷大,并且比较是无序的。 MSB对齐将允许甚至来自不同的二进制格式信号的直接比较。因此将NaN两个具有相同的信号组将是等效和赋予意义平等。

由于数学是其中数字“只是存在”领域。 在计算你必须的初始化的这些数字和的继续的根据您的需要它们的状态。 在这些昔日的内存初始化中,你永远无法靠工作方式。你永远无法让自己去想这个的“哦,那将与0XCD所有,我的算法中不会爆发时被初始化” 的。

所以,你需要适当的非混合的溶剂中的粘性足够的不不会让你的算法得到吸进和破碎。 涉及的具有优良的算法大多要与关系的工作,而这些的如果()的关系将被省略。

这就是油脂,你可以在创建投入新的变量,而不是从电脑存储器编程随机地狱。和你的算法不管它是什么,不会打破。

接下来,当你还是突然找出你的算法是生产NaN的,有可能清理出来,寻找到每一个分支一次一个。再次,“永远为假”规则正在帮助很多在此。

有关我,解释它的最简单的方法是:

  

我有一些东西,如果它不是一个苹果,然后是橙色的?

您无法用别的东西(甚至本身)比较NaN的,因为它不具有价值。此外,它可以是任何值(除了数字)。

  

我有事情,如果它不等于一个号码,然后它是一个字符串?

非常短的答案:

由于以下内容: nan / nan = 1 一定不能保留。否则inf/inf将是1。

(因此nan不能等于nan。至于><,如果nan会尊重在满足阿基米德属性的一组任意的顺序关系,我们将在极限再次nan / nan = 1)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top