我是 IEEE-754 委员会的成员,我会尽力帮助澄清一些事情。
首先,浮点数不是实数,浮点运算不满足实数运算的公理。三分法不是真正算术的唯一属性,它不适用于浮点数,甚至不是最重要的属性。例如:
- 加法不是关联的。
- 分配法则不成立。
- 有没有倒数的浮点数。
我可以继续。不可能指定一个固定大小的算术类型来满足我们所知道和喜爱的实数算术的所有属性。754 委员会必须决定弯曲或破坏其中的一些。这是由一些非常简单的原则指导的:
- 如果可以,我们会匹配真实算术的行为。
- 当我们做不到时,我们会尝试使违规行为尽可能可预测且易于诊断。
关于您的评论“这并不意味着正确答案是错误的”,这是错误的。谓词(y < x)
询问是否y
小于x
。如果y
是 NaN,那么它不小于任何浮点值x
,所以答案必然为假。
我提到三分法不适用于浮点值。但是,有一个类似的属性确实成立。754-2008 标准第 2 款第 5.11 条:
可能有四种互斥关系:小于、等于、大于和无序。最后一种情况出现在至少一个操作数是 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)
)。
更重要的是,在 8087 算术中将 NaN 形式化的时候并没有isnan( )
谓词;有必要为程序员提供一种方便有效的检测 NaN 值的方法,这种方法不依赖于isnan( )
可能需要很多年的编程语言。我将引用 Kahan 自己关于该主题的文章:
如果没有办法摆脱 NaN,它们将与 CRAY 上的 Indefinites 一样无用;一旦遇到,最好立即停止计算,而不是无限期地继续下去,得出一个无限期的结论。这就是为什么对 NaN 的某些操作必须提供非 NaN 结果的原因。哪些操作?...例外是 C 谓词“x == x”和“x!= x”,对于每个无限或有限数 x,它们分别为 1 和 0,但如果 x 不是数字 (NaN),则相反;在缺少 NaN 词和谓词 IsNaN(x) 的语言中,这些提供了 NaN 和数字之间唯一简单的无异常区别。
请注意,这也是排除返回“Not-A-Boolean”之类的逻辑的逻辑。也许这种实用主义是错误的,标准应该要求isnan( )
,但这将使 NaN 在几年内几乎不可能高效和方便地使用,而世界都在等待编程语言的采用。我不相信这是一个合理的权衡。
坦率地说: NaN == NaN 的结果现在不会改变。与其在互联网上抱怨,不如学会忍受它。如果您想争论适合容器的顺序关系也应该存在,我建议您主张您最喜欢的编程语言实现totalOrder
IEEE-754 (2008) 中标准化的谓词。事实上,它还没有说明 Kahan 的担忧的有效性,而这种担忧推动了当前的事态发展。