【问题标题】:Why does Double.NaN==Double.NaN return false?为什么 Double.NaN==Double.NaN 返回 false?
【发布时间】:2012-02-07 20:04:32
【问题描述】:

我只是在研究 OCPJP 问题,发现了这个奇怪的代码:

public static void main(String a[]) {
    System.out.println(Double.NaN==Double.NaN);
    System.out.println(Double.NaN!=Double.NaN);
}

当我运行代码时,我得到了:

false
true

当我们比较两个看起来相同的东西时,false 的输出如何? NaN 是什么意思?

【问题讨论】:

  • 这真的很奇怪。因为 Double.NaN 是静态 final,所以与 == 的比较应该返回 true。为问题 +1。
  • python中也是这样:In [1]: NaN==NaN Out[1]: False
  • 正确遵循 IEEE 754 标准的所有语言都是如此。
  • 直觉:“Hello”不是数字,true(布尔)也不是数字。 NaN != NaN 出于同样的原因 "Hello" != true
  • @Stephan:如果Double.NaN 的类型为java.lang.Double,则与Double.NaN==Double.NaN 的比较确实应该返回true。但是,它的类型是原语 double,并且适用于 double 的运算符规则(要求这种不等式以符合 IEEE 754,如答案中所述)。

标签: java floating-point nan scjp ocpjp


【解决方案1】:

NaN 是一个特殊值,表示“不是数字”;它是某些无效算术运算的结果,例如sqrt(-1),并且具有NaN != NaN 的(有时令人讨厌的)属性。

【讨论】:

    【解决方案2】:

    NaN 的意思是“不是数字”。

    Java Language Specification (JLS) Third Edition says:

    上溢的运算产生有符号无穷大,下溢的运算产生非规格化值或有符号零,而没有数学确定结果的运算产生 NaN。所有以 NaN 作为操作数的数值运算都会产生 NaN 作为结果。如前所述,NaN 是无序的,因此涉及一个或两个 NaN 的数值比较操作会返回 false,而涉及 NaN 的任何 != 比较都会返回 true,包括 x!=xx 为 NaN 时。

    【讨论】:

    • @nibot:基本正确。任何与符合 IEEE 的浮点数的比较都会产生 false。因此,该标准与 Java 的不同之处在于 IEEE 要求 (NAN != NAN) == false
    • 打开这个潘多拉魔盒 - 你在哪里看到“IEEE 要求 (NAN != NAN) == false”?
    【解决方案3】:

    javadoc for Double.NaN 说明了一切:

    一个保持double 类型的非数字 (NaN) 值的常量。相当于Double.longBitsToDouble(0x7ff8000000000000L)返回的值。

    有趣的是,Double 的来源定义了NaN

    public static final double NaN = 0.0d / 0.0;
    

    您描述的特殊行为是硬连线到 JVM 中的。

    【讨论】:

    • 是在 JVM 中硬连线,还是如 Peter 所说的那样由 CPU 实现?
    【解决方案4】:

    Not a number 表示操作的结果,其结果不能用数字表示。最著名的操作是 0/0,其结果未知。

    因此,NaN 不等于任何值(包括其他非数字值)。欲了解更多信息,请查看维基百科页面:http://en.wikipedia.org/wiki/NaN

    【讨论】:

    • -1:它确实代表0/0的结果。 0/0 始终是 NaN,但 NaN 可以是其他操作的结果 - 例如 2+NaN: an operation that has no mathematically definite result produces NaN,根据 @AdrianMitev 的回答
    • 确实,NaN 代表“非数字”,它是所有具有未定义或不可表示值的操作的结果。最著名和最常见的操作是 0/0,但显然还有大量其他操作具有相同的结果。我同意我的答案可以改进,但我不同意 -1... 我刚刚检查过维基百科也使用 0/0 操作作为第一个操作示例,其中包含 NaN 结果 (en.wikipedia.org/wiki/NaN)。
    • 另外,这是 Double 的 Java 源代码:public static final double NaN = 0.0d / 0.0;
    • @Matteo +0,现在错误的声明已经消失了。我的 -1 或 +1 不是让你同意或不同意的;但最好留下带有-1 的评论,以便作者能够理解为什么他的答案被认为是无用的 - 如果他愿意,可以更改它。
    • @Guillaume 如果该评论是针对我的,请改写:我不明白。
    【解决方案5】:

    为什么会有这种逻辑

    NaN 表示Not a Number。什么不是数字?任何事物。您可以在一侧拥有任何东西,而在另一侧拥有任何东西,因此无法保证两者是平等的。 NaN 是用 Double.longBitsToDouble(0x7ff8000000000000L) 计算的,正如您在 longBitsToDouble 的文档中看到的那样:

    如果参数是0x7ff0000000000001L 到范围内的任何值 0x7fffffffffffffffL 或范围 0xfff0000000000001L0xffffffffffffffffL,结果是NaN

    另外,NaN 在 API 内部进行逻辑处理。


    文档

    /** 
     * A constant holding a Not-a-Number (NaN) value of type
     * {@code double}. It is equivalent to the value returned by
     * {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
     */
    public static final double NaN = 0.0d / 0.0;
    

    顺便说一句,NaN作为您的代码示例进行了测试:

    /**
     * Returns {@code true} if the specified number is a
     * Not-a-Number (NaN) value, {@code false} otherwise.
     *
     * @param   v   the value to be tested.
     * @return  {@code true} if the value of the argument is NaN;
     *          {@code false} otherwise.
     */
    static public boolean isNaN(double v) {
        return (v != v);
    }
    

    解决方案

    你可以做的是使用compare/compareTo:

    Double.NaN 被此方法认为与自身相等 并且大于所有其他 double 值(包括 Double.POSITIVE_INFINITY)。

    Double.compare(Double.NaN, Double.NaN);
    Double.NaN.compareTo(Double.NaN);
    

    或者,equals

    如果thisargument 都代表Double.NaN,那么 equals 方法返回 true,即使 Double.NaN==Double.NaN 的值为 false

    Double.NaN.equals(Double.NaN);
    

    【讨论】:

    • 你知道有什么情况下NaN != NaN 为假会使程序比NaN != NaN 为真更复杂吗?我知道 IEEE 很久以前就做出了这个决定,但从实际的角度来看,我从未见过它有用的案例。如果一个操作应该一直运行到连续迭代产生相同的结果,如果不是这种行为,连续两次迭代产生 NaN 将被“自然地”检测为退出条件。
    • @supercat 你怎么能说两个随机非数自然相等?或者说,原始平等?将 NaN 视为一个实例,而不是原始的东西。每个不同的异常结果都是奇怪事物的不同实例,即使两者应该表示相同,对不同的实例使用 == 也必须返回 false。另一方面,当使用 equals 时,可以按照您的意愿正确处理。 [docs.oracle.com/javase/7/docs/api/java/lang/…
    • @falsarella:问题不在于两个随机数是否应该被视为“绝对相等”,而是在什么情况下让任何数字与自身“绝对不相等”比较有用。如果一个人试图计算f(f(f...f(x))) 的极限,并且发现一些ny=f[n](x) 使得f(y) 的结果与y 无法区分,那么y 将与结果无法区分任何嵌套更深的f(f(f(...f(y)))。即使有人希望NaN==NaN 为假,对于某些x,让Nan!=Nan also 为假不会比让x!=x 为真更“令人惊讶”。
    • @falsarella:我认为Double.NaN的类型不是Double,而是double,所以这个问题与double的行为有关。尽管存在可以测试涉及double 值的等价关系的函数,但我所知道的“为什么”(这是原始问题的一部分)的唯一令人信服的答案是“因为 IEEE 的一些人不认为平等-测试应该定义一个等价关系”。顺便说一句,是否有任何简洁的惯用方法来测试 xy 是否仅使用原始运算符进行等效?我所知道的所有配方都相当笨重。
    • 最佳而简单的答案。谢谢
    【解决方案6】:

    根据定义,NaN 不等于包括 NaN 在内的任何数字。这是 IEEE 754 标准的一部分,由 CPU/FPU 实现。这不是 JVM 必须添加任何逻辑来支持的东西。

    http://en.wikipedia.org/wiki/NaN

    与 NaN 的比较总是返回一个无序的结果,即使与它自身进行比较也是如此。 ... 等式和不等式谓词是无信号的,因此 x = x 返回 false 可用于测试 x 是否为安静的 NaN。

    Java 将所有 NaN 视为安静的 NaN。

    【讨论】:

    • 它是由 CPU 实现的,还是像 Bohemian 提到的那样在 JVM 中硬连线?
    • JVM 必须调用能正确实现它的任何东西。在 PC 上,CPU 完成所有工作。在没有这种支持的机器上,JVM 必须实现它。 (我不知道任何这样的机器)
    • 在选择 8087 的那一天,C 库包含一个 FP 仿真器。无论哪种方式,像 JVM 这样的程序都不必担心。
    【解决方案7】:

    按照 The IEEE standard for floating point arithmetic 表示双精度数字,

    IEEE 双精度浮点标准表示 需要一个 64 位的字,可以表示为从 0 到 63,从左到右

    哪里,

    S: Sign – 1 bit
    E: Exponent – 11 bits
    F: Fraction – 52 bits 
    

    如果E=2047(所有E都是1)且F非零,则V=NaN(“不是数字”)

    这意味着,

    如果所有E 位都是1,并且如果F 中有任何非零位,则该数字为NaN

    因此,除其他外,以下所有数字均为NaN

    0 11111111 0000000000000000010000000000000000000000000000000000 = NaN
    1 11111111 0000010000000000010001000000000000001000000000000000 = NaN
    1 11111111 0000010000011000010001000000000000001000000000000000 = NaN
    

    尤其是你不能测试

    if (x == Double.NaN) 
    

    检查特定结果是否等于Double.NaN,因为所有“非数字”值都被认为是不同的。但是,您可以使用Double.isNaN 方法:

    if (Double.isNaN(x)) // check whether x is "not a number"
    

    【讨论】:

      【解决方案8】:

      这可能不是问题的直接答案。 但是如果你想检查某个东西是否等于Double.NaN,你应该使用这个:

      double d = Double.NaN
      Double.isNaN(d);
      

      这将返回true

      【讨论】:

        【解决方案9】:

        根据这个link,它有各种情况,很难记住。这就是我记忆和区分它们的方式。 NaN 表示“数学上未定义”,例如:“0 除以 0 的结果是未定义的”,因为它是未定义的,所以“与未定义相关的比较当然是未定义的”。此外,它更像数学前提。另一方面,正无穷和负无穷都是预定义和确定的,例如“正无穷大或负无穷大在数学上是很好定义的”。

        【讨论】:

          【解决方案10】:

          如果你有变量

          Double a = Double.NaN
          

          使用

          String.valueOf(Double.NaN) == a.toString()
          

          【讨论】:

            猜你喜欢
            • 2011-07-09
            • 2010-12-19
            • 2010-11-11
            • 1970-01-01
            • 2017-10-06
            • 1970-01-01
            • 2010-10-08
            • 2013-01-04
            • 1970-01-01
            相关资源
            最近更新 更多