首页 文章

对于IEEE754 NaN值,所有比较返回false的理由是什么?

提问于
浏览
220

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

我想这可以通过某种方式简化数值计算,但我找不到明确说明的理由,即使在Kahan的Lecture Notes on the Status of IEEE 754中也没有详细讨论其他设计决策 .

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

Edit: 到目前为止,答案都认为比较NaNs毫无意义 .

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

因此,在我看来,选择返回真或假的比较是任意的,对于一般数据处理,如果它遵循通常的定律(==的反射性,<= =,>的三分法),那将是有利的,以免数据结构依赖这些法律变得困惑 .

因此,我要求打破这些法律的一些具体优势,而不仅仅是哲学推理 .

Edit 2: 我想我现在明白了为什么制作NaN最大值会是一个坏主意,它会搞砸上限的计算 .

可能需要NaN!= NaN以避免检测环路中的收敛,例如

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

然而,最好通过比较绝对差异与小限制来编写 . 所以恕我直言,这是打破NaN反身性的一个相对弱的论据 .

13 回答

  • 0

    再说一个类比 . 如果我递给你两个盒子,告诉你它们都没有苹果,你会告诉我盒子里面有同样的东西吗?

    NaN不包含有关什么是什么的信息,而不包含什么 . 因此,这些元素绝对不能说是平等的 .

  • 4

    我是IEEE-754委员会的成员,我会尽力帮助澄清一些事情 .

    首先,浮点数不是实数,浮点运算不满足实数算术的公理 . 三分法不是真正算术的唯一属性,它不适用于浮点数,甚至也不是最重要的 . 例如:

    • 加法不是关联的 .

    • 分配法并不成立 .

    • 有没有反转的浮点数 .

    我可以继续不可能指定一个固定大小的算术类型来满足我们所熟知和喜爱的所有实际算术属性 . 754委员会必须决定弯曲或打破其中一些 . 这是由一些非常简单的原则指导:

    • 当我们可以时,我们匹配实际算术的行为 .

    • 当我们做不到时,我们会尽量使违规行为变得可预测并且易于诊断 .

    关于你的评论"that doesn't mean that the correct answer is false",这是错误的 . 谓词 (y < x) 询问 y 是否小于 x . 如果 y 是NaN,那么它不小于任何浮点值 x ,因此答案必定为false .

    我提到三分法不适用于浮点值 . 但是,有一个类似的属性确实成立 . 754-2008标准第5.11条第2款:

    四种相互排斥的关系是可能的:小于,等于,大于和无序 . 当至少一个操作数是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) ) .

    • 更重要的是,当NaN在8087算术中形式化时,没有 isnan( ) 谓词;有必要为程序员提供一种方便有效的方法来检测NaN值,这些值不依赖于编程语言,这可能需要很长时间才能提供类似于_1400547的内容 . 我将引用卡汉自己关于这个主题的文章:

    如果没有办法摆脱NaNs,它们就像CRAYs上的Indefinites一样无用;一旦遇到一个人,计算将最好停止,而不是无限期地持续到无限期结束 . 这就是为什么NaN上的某些操作必须提供非NaN结果的原因 . 哪个操作? ......例外是C谓词“x == x”和“x!= x”,对于每个无限或有限数x,它们分别为1和0,但如果x不是数字(NaN)则反向;这些提供了NaNs与缺少NaN单词和谓词IsNaN(x)的语言中数字之间唯一的简单区分 .

    请注意,这也是排除返回“Not-A-Boolean”之类的逻辑 . 也许这种实用主义是错误的,标准应该需要 isnan( ) ,但这将使NaN几乎不可能在世界等待编程语言采用的同时有效和方便地使用几年 . 我不相信那是一次合理的权衡 .

    直言不讳:NaN == NaN的结果现在不会改变 . 更好地学会忍受它而不是在互联网上抱怨 . 如果你想争论一个适合容器的订单关系也应该存在,我建议你提倡你最喜欢的编程语言实现IEEE-754(2008)标准化的 totalOrder 谓词 . 事实上,它还没有谈到Kahan关注的有效性,这种关注促成了当前的事态 .

  • -1

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

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

    在将sqrt(-3)与sqrt(-2)进行比较的情况下,此行为也很有用 . 他们都会返回NaN,但即使他们返回相同的值,他们也不是等价的 . 因此,在处理NaN时,等式总是返回false是期望的行为 .

  • 12

    NaN上的维基百科文章中,以下做法可能会导致NaN:

    • 所有数学运算>使用NaN作为至少一个操作数

    • 分区0/0,∞/∞,∞/-∞,-∞/∞和-∞/-∞

    • 乘法0×∞和0×-∞

    • 加法∞(-∞),( - ∞)∞和等效减法 .

    • 将函数应用于其域外的参数,包括取负数的平方根,取负数的对数,取90度(或π/ 2弧度)的奇数倍的正切,或取反数字小于-1或大于1的正弦或余弦 .

    由于无法知道哪些操作创建了NaN,因此没有办法比较它们是否有意义 .

  • 447

    我不知道设计原理,但这里摘自IEEE 754-1985标准:

    “即使操作数的格式不同,也应该可以比较所有支持格式的浮点数 . 比较是精确的,永远不会溢出或下溢 . 四种相互排斥的关系是可能的:小于,等于,大于和无序当最后一种情况出现时,至少有一个操作数是NaN . 每个NaN都应该将无序与所有事物进行比较,包括它自己 . “

  • 1

    它看起来很奇怪,因为大多数允许NaN的编程环境也不允许使用3值逻辑 . 如果将3值逻辑抛入混合中,它将变得一致:

    • (2.7 == 2.7)=真

    • (2.7 == 2.6)= false

    • (2.7 == NaN)=未知

    • (NaN == NaN)=未知

    即使.NET没有提供 bool? operator==(double v1, double v2) 运算符,所以你仍然坚持使用愚蠢的 (NaN == NaN) = false 结果 .

  • 45

    我猜NaN(非数字)意味着:这不是一个数字,因此比较它并没有多大意义 .

    这有点像SQL中的算术 null 操作数:它们都导致 null .

    浮点数的比较比较数值 . 因此,它们不能用于非数值 . 因此,无法在数字意义上比较NaN .

  • 0

    过于简化的答案是NaN没有数值,因此没有任何东西可以与其他任何东西进行比较 .

    如果您希望它们像INF一样运行,您可以考虑使用INF测试和替换您的NaN .

  • 2

    虽然我同意将NaN与任何实数进行比较应该是无序的,但我认为将NaN与其自身进行比较是有正当理由的 . 例如,人们如何发现信号NaNs和安静的NaNs之间的区别?如果我们将信号视为一组布尔值(即位向量),则可能很好地询问位向量是相同还是不同并相应地对这些组进行排序 . 例如,在解码最大偏差指数时,如果有效位被左移,以便在二进制格式的最高有效位上对齐有效数字的最高有效位,则负值将是安静的NaN,任何正值都将是信号NaN . 零当然是为无穷大保留的,比较将是无序的 . MSB对齐将允许甚至来自不同二进制格式的信号的直接比较 . 因此,具有相同信号集的两个NaN将是等价的并且赋予相等的含义 .

  • 30

    NaN是一个隐式的新实例(一种特殊的运行时错误) . 这意味着 NaN !== NaN 出于同样的原因 new Error !== new Error ;

    并且请记住,这种隐含性也可以在外部错误中看到,例如在正则表达式的上下文中它意味着 /a/ !== /a/ 这只是 new RegExp('a') !== new RegExp('a') 的语法糖

  • -2

    因为数学是数字"just exist"的领域 . 在计算中,您必须初始化这些数字并根据您的需要保持其状态 . 在那些过去,内存初始化以您永远不会依赖的方式工作 . 你永远不会让自己想到这个"oh, that would be initialized with 0xCD all the time, my algo will not broke" .

    所以你需要适当的非混合溶剂,这种溶剂足够粘稠,不会让你的算法被吸进和破坏 . 涉及数字的好算法主要用于关系,而if()关系将被省略 .

    这只是你可以在创建时加入新变量的油脂,而不是从计算机内存编程随机地狱 . 而你的算法无论如何都不会破坏 .

    接下来,当您仍然突然发现您的算法正在生成NaN时,可以将其清除,一次查看每个分支 . 同样,“永远错误”的规则对此有很大帮助 .

  • -2

    对我来说,最简单的解释方法是:

    我有东西,如果它不是苹果,那么它是橙色的吗?

    您无法将NaN与其他东西(甚至是其自身)进行比较,因为它没有值 . 它也可以是任何值(数字除外) .

    我有一些东西,如果它不等于一个数字那么它是一个字符串?

  • 0

    答案非常简短:

    因为以下内容: nan / nan = 1 必须不能保留 . 否则 inf/inf 将为1 .

    (因此 nan 不能等于 nan . 至于 >< ,如果 nan 会尊重满足阿基米德 property 的集合中的任何订单关系,我们将再次使用 nan / nan = 1 ) .

相关问题