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

我认为这在某种程度上简化了数值计算,但我找不到一个明确的原因,甚至在Kahan的《IEEE 754状态讲义》中也找不到,他详细讨论了其他设计决策。

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

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

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

所以在我看来,选择返回真或假进行比较是随意的, 对于一般的数据处理来说,如果它符合通常的规律,那将是有利的 (==的反身性,<,==,>的三分), 以免依赖这些定律的数据结构变得混乱。

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

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

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

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

但是最好是通过比较小极限下的绝对差来写。 所以恕我直言,这是一个相对较弱的论点,打破自反性在NaN。


当前回答

我猜NaN(不是一个数字)的意思就是:这不是一个数字,因此比较它是没有意义的。

这有点像SQL中带有空操作数的算术:它们的结果都为空。

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

其他回答

我猜NaN(不是一个数字)的意思就是:这不是一个数字,因此比较它是没有意义的。

这有点像SQL中带有空操作数的算术:它们的结果都为空。

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

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

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

MeToo来这里是为了了解其中的道理,为什么NaN == NaN = false。

读完(几乎)所有的内容后,我仍然感到困惑,为什么a == NaN不能取代像isNaN()这样的函数,因为这似乎是如此明显。

但事情并没有那么简单。

还没有人提到矢量几何。但很多计算是在2维或3维中进行的,所以在向量空间中。

在思考了一会儿之后,我立刻意识到,为什么让NaN不与自己比较是一件好事。希望其他人也能很容易理解下面的内容。

向量

恕我直言,在NaN出现之前还需要一段时间。 首先让我为那些不太懂数学的人解释一下

在向量几何中,我们通常使用复数。

复数由两个浮点(A + bi)组成(其中i表示虚数,i * i == -1),这允许我们在2维平面上求解所有点。使用浮点数,我们不能表示每个值,所以我们必须近似地表示一个位。因此,如果我们将这些值四舍五入到我们可以表示的某个值,我们仍然可以尝试创建数值稳定的算法,这将为我们提供一些我们想要存档的东西的良好近似。

进入无穷

这里还没有NaN。请耐心等待。稍后我将在下面谈到这一点。

如果我们想指定一个很远很远的点,我们可以留下我们可以表示的数字的范围,结果是无穷大。在IEEE浮点数中,幸运的是我们有+inf(我写为inf)或-inf(写为-inf)。

这很好:

A +∞I是有意义的,对吧?它是x轴上a点和y轴上"正无穷"点的向量。等一下,我们说的是带菌者!

向量有原点和指向点。归一化向量是从位置(0,0)开始的。

现在考虑一个原点为(0,0)指向(a,inf)的向量。

还说得通吗?不完全是。当我们仔细观察时,我们会发现,规范化向量(0,inf)是相同的向量!由于向量是如此之长,a在无穷中的推导就看不见了。或者换种说法:

对于笛卡尔坐标系中的无限长向量,有限轴可以表示为0,因为我们允许近似(如果不允许近似,我们就不能使用浮点数!)

所以替换向量(0,无穷)仍然是合适的。事实上,任何(x,无穷)都可以代替有限的x,那么为什么不用归一化向量原点的0呢?

那么我们得到了什么?好吧,在我们的向量中允许inf,我们实际上得到了8个可能的无限向量,每个都旋转了45度(括号中是度):

(正,0)(0)(正、正)(45),(0,正)(90),(负、正)(135),(负无穷,0)(180),(负无穷,无穷)(225),(0,无穷)(270)和(正、负)(315)

这一切都没有造成任何麻烦。事实上,能够表达有限向量以外的东西是很好的。这样我们就可以自然地扩展我们的模型。

极坐标

这里还是没有NaN,但我们越来越近了

上面我们用复数作为笛卡尔坐标。但是复数还有第二种写法。这就是极坐标。

极坐标由长度和角度组成,比如[角度,长度]。如果我们把复数转换到极坐标中,我们会发现,我们可以用[angle,inf]表示比8个角要多一点的角。

因此,如果你想创建一个数学模型,它允许在某个多维空间中无限长的向量,你肯定想在你的计算中尽可能地使用极坐标。

你所要做的就是把笛卡尔坐标转换成极坐标,反之亦然。

如何做到这一点,留给读者作为练习。

进入南

现在,我们有什么?

我们有一个用极坐标计算的数学模型。 我们有一些输出设备,可能使用笛卡尔坐标。

我们现在要做的是能够在这两者之间进行转换。我们需要做什么?

当然,我们需要浮点数!

由于我们可能需要计算一些千万亿的坐标,(也许我们要渲染一些天气预报,或者从大型强子对撞机获得一些碰撞数据),我们不希望包括缓慢且容易出错的错误处理(WTF?容易出错的错误处理?在所有这些复杂的数学(希望数值稳定)步骤中。

那么我们如何传播误差呢?

正如IEEE所说:我们使用NaN进行错误传播

这是什么情况呢?

在极坐标空间中的一些计算 转换到笛卡尔空间 如果有什么事情失败了,我不会去营救

这就导致了。

. .为什么NaN == NaN一定是假的

为了解释这一点,让我们先将这个复杂的东西简化为笛卡尔坐标下2个向量的简单结果:

(a,b)及(c,d)

我们想比较这两个。这是这个比较的样子:

A == c && b == d

到目前为止一切都正确吗?

是的。但直到我们观察到以下两个极向量它们可能是我们的两个笛卡尔向量的来源:

[南,恩夫]和[0,南]

当然这两个向量在极坐标空间中是不相等的。但转换到笛卡尔空间后,两者都是:

(南,南)和(南,南)

那么,它们应该突然相等比较吗?

肯定不是!

感谢IEEE定义NaN == NaN必须返回false,我们非常原始的向量比较仍然给了我们预期的结果!

我认为,这正是IEEE定义它的动机。

现在我们得忍受这烂摊子。但这真的是一团糟吗?我犹豫不决。但至少,我现在能理解其中的(可能的)推理了。

希望我没有错过什么。

临终遗言

当涉及到浮点数时,比较事物的原始方法通常并不完全合适。

在浮点数中,通常不使用==,而是使用abs(a-b) < eps,其中eps是一个非常小的值。这是因为像1/3 + 1/3 * 2.0 == 1.0这样的东西可能并不正确,这取决于您运行的硬件。

1/3 + 1/3 * 2.0 == 1/3 + 1/3 + 1/3在所有合理的硬件上应该是正确的。所以可以使用even ==。只有仔细。但不排除这种可能性。

然而,这并不意味着上述推理无效。因为上面并不是一个数学证明,IEEE是正确的。这只是一个例子,它可以让你理解背后的原因,以及为什么最好这样定义它。

即使它是一个面向所有像我这样的编程人员的PITA。

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

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

这种行为在比较根号(-3)和根号(-2)的情况下也很有利。它们都会返回NaN,但它们并不等效,即使它们返回相同的值。因此,在处理NaN时,具有相等总是返回false是理想的行为。

它看起来很奇怪,因为大多数允许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结果所困扰。