15
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()方法的实际工作是什么。

4

4 回答 4

3

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

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 方法的 Javadoc 中有记录:

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

于 2012-06-13T05:49:10.587 回答
2

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

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

于 2012-06-13T06:01:03.550 回答
2

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

在大多数情况下,Java 平台将给定类型的 NaN 值视为折叠为单个规范值(因此本规范通常将任意 NaN 称为规范值)。但是,Java 平台 1.3 版引入了使程序员能够区分 NaN 值的方法:Float.floatToRawIntBitsandDouble.double- ToRawLongBits方法。感兴趣的读者可以参考FloatDouble类的规范以获取更多信息。

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

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

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

于 2012-06-13T06:07:29.853 回答
1

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

于 2012-06-13T05:58:23.507 回答