【问题标题】:Confusion on NaN in JavaJava中对NaN的困惑
【发布时间】:2012-06-16 01:10:58
【问题描述】:
int i = 0, j = 0;
double nan1 = (double)0/0;
double nan2 = (double)0/0;
double nan3 = (double)i/j;
System.out.println(Double.doubleToRawLongBits(nan1) == Double.doubleToRawLongBits(nan2));
System.out.println(Double.doubleToRawLongBits(nan1) == Double.doubleToRawLongBits((double)0/0));
System.out.println(Double.doubleToRawLongBits(nan3) == Double.doubleToRawLongBits(nan2));

输出:

true
true
false

请帮助我如何输出前两个的true 和最后一个的false。请告诉我 Double.doubleToRawLongBits() 方法的实际工作是什么。

【问题讨论】:

  • 正在生成两个不同的NaNs。一个是0x7ff8000000000000,另一个是0xfff8000000000000。硬件似乎为0 / 0 提供了0xfff8000000000000。但是编译器的不断传播是使用0x7ff8000000000000 来代替。除非我忽略了什么,否则这对我来说就像一个错误。
  • 如果为方法启用strictfp会怎样?
  • 此外: »在符合 IEEE 754 标准的浮点存储格式中,NaN 由特定于 NaN 的预定义位模式标识。符号位无关紧要。« (Wikipedia)
  • 你能澄清一下你对什么感到困惑吗?这是一个问题,为什么 NaN 可以有不同的位模式,或者为什么在涉及相同值的两种情况下产生的位模式不同?
  • @DanielPryden 我想你理解我真正的困惑。我的疑问是为什么第三个打印语句给出错误,尽管 nan3 和 nan2 都是使用相同的数字生成的,即通过执行 0/0。请回答我,我不明白已经给出的答案。

标签: java floating-point double nan


【解决方案1】:

请尝试运行以下代码查看值:

public class Test
{
    public static void main(String[] args){
        int i = 0, j = 0;
        double nan1 = (double)0/0;
        double nan2 = (double)0/0;
        double nan3 = (double)i/j;
        System.out.println(Double.doubleToRawLongBits(nan1) + " == "+ Double.doubleToRawLongBits(nan2) + " is " +
            (Double.doubleToRawLongBits(nan1) == Double.doubleToRawLongBits(nan2)));
        System.out.println(Double.doubleToRawLongBits(nan1) + " == "+ Double.doubleToRawLongBits((double)0/0) + " is " +
            (Double.doubleToRawLongBits(nan1) == Double.doubleToRawLongBits((double)0/0)));
        System.out.println(Double.doubleToRawLongBits(nan3) + " == "+ Double.doubleToRawLongBits(nan2) + " is " +
            (Double.doubleToRawLongBits(nan3) == Double.doubleToRawLongBits(nan2)));
    }
}

在我的 Mac 上,它会产生以下输出:

9221120237041090560 == 9221120237041090560 is true
9221120237041090560 == 9221120237041090560 is true
-2251799813685248 == 9221120237041090560 is false

doubleToRawLongBits method 的 Javadoc 中记录了这个陷阱:

如果参数是 NaN,则结果是表示实际 NaN 值的长整数。与 doubleToLongBits 方法不同,doubleToRawLongBits 不会将所有编码 NaN 的位模式折叠为单个“规范”NaN 值。

【讨论】:

  • 这能回答问题吗?
  • 代码与问题中的代码相同,所以相当多余;但是按照文档的链接确实回答了这个问题,尽管有一个间接的。我将相关部分编辑为引用。但我想说,上面的一切都是垃圾,与答案无关。
  • 啊。好吧。我应该删除这个答案。 :)
  • 未删除,因为我认为目前它的答案(乔伊的编辑)比其他答案更好。
【解决方案2】:

原因是当你将双变量 0 除以 0 时返回 NaN,因此该方法没有二进制的单一规范表示,因此它可能返回 NaN 的二进制为 7F F8 00 00 00 00 00 00 或FF F8 00 00 00 00 00 00。

尽管从技术上讲,它们表示相同的东西,即 NaN,但它在二进制表示上是不同的。

【讨论】:

    【解决方案3】:

    IEEE 754 标准允许NaN 使用不同的位模式。出于计算和比较的目的,它们都应该以相同的方式工作(即NaN 比较不等于自身,没有排序,并且涉及NaN 的每个计算都是NaN 本身)。使用doubleToRawLongBits,您可以获得使用的确切位模式。这在 JLS 中也有详细说明:

    在大多数情况下,Java 平台将给定类型的 NaN 值视为 虽然折叠成一个单一的规范值(因此本规范也不是- mally 指代任意 NaN,就好像指代规范值一样)。不过,版本 1.3 Java平台引入了让程序员区分的方法 在 NaN 值之间:Float.floatToRawIntBitsDouble.double- ToRawLongBits 方法。有兴趣的读者可以参考规范 对于 FloatDouble 类以了解更多信息。

    在您的情况下,符号位不同,在这种情况下,我可以将您定向到Wikipedia,简明扼要地总结了这一点:

    在符合 IEEE 754 标准的浮点存储格式中,NaN 由特定于 NaN 的预定义位模式标识。符号位无关紧要。

    您的两个值都是NaN,它们只是使用不同的位来表示。 IEEE 754 允许的东西,在这种情况下,可能源于编译器用Double.NaN 替换导致NaN 的常量计算,而实际硬件给出不同的结果,正如Mysticial 在对已经有问题了。

    【讨论】:

    • Joey 是编译器为 NaN 值随机选择不同的位模式。如果不是,那么为什么 nan1 和 nan2 的模式相同,但 nan3 的模式不同。
    【解决方案4】:

    我认为 Java 遵循 IEEE 754。在这种情况下,NaN 具有不止一种可能的位表示。您的情况下的两种表示形式在“符号”位上有所不同。符号位的值似乎没有被标准定义,通常被忽略。所以这两个值都是正确的。见http://en.wikipedia.org/wiki/NaN

    【讨论】:

      猜你喜欢
      • 2020-11-11
      • 2014-03-29
      • 2013-10-28
      • 1970-01-01
      • 1970-01-01
      • 2018-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多