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。