我是IEEE-754委员会的成员,我会试着帮助澄清一些事情。
首先,浮点数不是实数,浮点算术不满足实数算术的公理。三分并不是真正算术中对浮点数不成立的唯一性质,甚至也不是最重要的性质。例如:
加法不是结合律。
分配律不成立。
有不带倒数的浮点数。
我还可以继续列举。不可能指定一个固定大小的算术类型来满足我们所知道和喜爱的真实算术的所有属性。754委员会必须决定改变或打破其中的一些。这是由一些非常简单的原则指导的:
当我们可以的时候,我们匹配真实算术的行为。
当我们做不到的时候,我们会尽量让违规行为变得可预测,并且尽可能容易诊断。
关于你所说的“这并不意味着正确答案是错误的”,这是错误的。谓词(y < x)询问y是否小于x。如果y是NaN,则它不小于任何浮点值x,因此答案必然为假。
我提到过三分法不适用于浮点值。然而,有一个类似的性质是成立的。754-2008标准第2段第5.11条:
可能存在四种互斥关系:小于、等于、大于和无序。当至少有一个操作数是NaN时,会出现最后一种情况。每个NaN都应该与包括自身在内的所有事物进行无序比较。
就编写额外的代码来处理nan而言,通常有可能(尽管并不总是容易)以正确的方式构建代码以使nan失败,但情况并非总是如此。如果不是,则可能需要一些额外的代码,但这只是代数闭包为浮点算术带来的便利所付出的小小代价。
附录:
许多评论者认为,保留等式的反身性和三分法会更有用,因为采用NaN != NaN似乎并没有保留任何熟悉的公理。我承认我对这个观点有一些同情,所以我想我应该重新审视这个答案,并提供更多的背景。
通过与Kahan的交谈,我的理解是NaN != NaN起源于两个务实的考虑:
That x == y should be equivalent to x - y == 0 whenever possible (beyond being a theorem of real arithmetic, this makes hardware implementation of comparison more space-efficient, which was of utmost importance at the time the standard was developed — note, however, that this is violated for x = y = infinity, so it’s not a great reason on its own; it could have reasonably been bent to (x - y == 0) or (x and y are both NaN)).
More importantly, there was no isnan( ) predicate at the time that NaN was formalized in the 8087 arithmetic; it was necessary to provide programmers with a convenient and efficient means of detecting NaN values that didn’t depend on programming languages providing something like isnan( ) which could take many years. I’ll quote Kahan’s own writing on the subject:
Were there no way to get rid of NaNs, they would be as useless as Indefinites on CRAYs; as soon as one were encountered, computation would be best stopped rather than continued for an indefinite time to an Indefinite conclusion. That is why some operations upon NaNs must deliver non-NaN results. Which operations? … The exceptions are C predicates “ x == x ” and “ x != x ”, which are respectively 1 and 0 for every infinite or finite number x but reverse if x is Not a Number ( NaN ); these provide the only simple unexceptional distinction between NaNs and numbers in languages that lack a word for NaN and a predicate IsNaN(x).
请注意,这也是排除返回“Not-A-Boolean”之类内容的逻辑。也许这种实用主义是错误的,标准应该要求isnan(),但这将使NaN在世界等待编程语言采用的几年里几乎不可能有效和方便地使用。我不相信这是一个合理的权衡。
坦率地说:NaN == NaN的结果现在不会改变。与其在网上抱怨,不如学着接受现实。如果你想证明适合容器的顺序关系也应该存在,我建议你提倡你最喜欢的编程语言实现IEEE-754(2008)中标准化的totalOrder谓词。事实上,它还没有证明卡汉的担忧是正确的,正是这种担忧促使了目前的事态。