为什么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不包含某物是什么,只包含它不是什么。因此,这些元素不能肯定地说相等。

其他回答

很简单的回答:

因为以下几点: Nan / Nan = 1 绝对不能持有。否则inf/inf等于1。

(因此nan不能等于nan。对于>或<,如果nan尊重满足阿基米德性质的集合中的任何顺序关系,我们将再次得到nan / nan = 1的极限)。

因为数学是数字“仅仅存在”的领域。 在计算中,你必须初始化这些数字,并根据需要保持它们的状态。 在过去的日子里,内存初始化的工作方式是你永远无法依赖的。你永远不能允许自己这样想“哦,它会一直用0xCD初始化,我的算法不会坏”。

所以你需要合适的非混合溶剂,足够粘稠,不会让你的算法被卷入和破坏。 涉及数字的优秀算法大多使用关系,而那些if()关系将被省略。

这只是油脂,你可以在创建新变量,而不是从计算机内存编程随机地狱。不管你的算法是什么,都不会崩溃。

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

While I agree that comparisons of NaN with any real number should be unordered, I think there is just cause for comparing NaN with itself. How, for example does one discover the difference between signaling NaNs and quiet NaNs? If we think of the signals as a set of Boolean values (i.e. a bit-vector) one might well ask whether the bit-vectors are the same or different and order the sets accordingly. For example, on decoding a maximum biased exponent, if the significand were left shifted so as to align the most significant bit of the significand on the most significant bit of the binary format, a negative value would be a quiet NaN and any positive value would be a signaling NaN. Zero of course is reserved for infinity and the comparison would be unordered. MSB alignment would allow for the direct comparison of signals even from different binary formats. Two NaNs with the same set of signals would therefore be equivalent and give meaning to equality.

过于简单的回答是,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。