在Java中比较浮点数会产生奇怪的结果

14

我真的无法理解为什么会发生以下情况:

Double d = 0.0;
System.out.println(d == 0); // is true
System.out.println(d.equals(0)); // is false ?!

然而,这个例子按预期工作:

Double d = 0.0;
System.out.println(d == 0.0); // true
System.out.println(d.equals(0.0)); // true
我很确定这与自动装箱有关,但我真的不知道为什么当使用`==`运算符和调用`.equals`时,`0`会被不同地装箱。

这不是隐式违反了`equals`协议吗?

  * 它是自反的:对于任何非空引用值x,
  * x.equals(x)应该返回true。

编辑:

谢谢快速回答。我想到它被装箱了,真正的问题是:为什么它被不同地装箱了?我的意思是如果`d == 0d`比`d.equals(0d)`更直观和预期,然而如果`d == 0`看起来像一个`Integer`则为`true`,那么“直观地”`d.equals(0)`也应该为true。

5个回答

20

将其更改为

System.out.println(d.equals(0d)); // is false ?! now true

你正在将 double 与整数类型的0进行比较。

底层实现

System.out.println(d.equals(0)); // is false ?!

0将自动装箱为Integer,并将一个Integer实例传递给Double类的equals()方法,在那里它会进行比较。

@Override
    public boolean equals(Object object) {
        return (object == this)
                || (object instanceof Double)
                && (doubleToLongBits(this.value) == doubleToLongBits(((Double) object).value));
    }

它肯定会返回false

更新

当您使用==进行比较时,它会比较值,因此无需自动装箱,它直接操作值。其中equals()接受Object,所以如果您尝试调用d1.equals(0)0不是对象,因此它将执行自动装箱并将其打包为Integer,这是一个对象。


@Simeon回答了你在评论中的问题。 - jmj
d 是一个对象而 0 是原始类型时,它如何比较值?我认为它要么将 d 拆箱(转换为 int?或者可能使用 Double.doubleValue()?),要么装箱 0,否则我不知道它如何对一个对象和一个原始类型执行 ==。也许我应该问一个单独的问题,因为这个问题有点复杂... - Simeon
1
它将调用 d.doubleValue() == 0,请在JLS 5.1.7中了解更多信息。 - jmj
@Paulo 是的 :) 它不会“触碰”原始 int,而是在 Double 对象上调用 d.doubleValue()。现在我在想这是否是 JVM 实现特定的,但这是另一个话题 :D - Simeon
1
@Simeon:这不是JVM特定的,因为它是由JLS指定的。 Jigar Joshi,抱歉之前的评论有误,我混淆了;你在谈论拆箱转换,但是你应该提到(http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.8)[JLS 5.1.8]。 - Blaisorblade
显示剩余2条评论

6

Number对象只有在它们属于相同类型时,才等于具有相同值的数字。也就是说:

new Double(0).equals(new Integer(0));
new BigInteger("0").equals(new BigDecimal("0"));

所有类似组合都是假的。

在您的情况下,字面量0被封装成一个Integer对象。


为什么在调用.equals时会将其装箱为Integer,而在调用==时会将其装箱为其他类型? - Simeon
@Simeon 因为 equals() 接受 Object,请查看这里 - jmj
@Jigar,我确实看了你的答案(我甚至点了赞),但让我困惑的是,当我们进行==比较时,0会被装箱为Double(或者不是这样吗?!在==比较中到底发生了什么,应该创建一个对象才能正确比较引用吧?)而当我们使用.equals时,它被装箱为Integer - Simeon

5

值得注意的是,你应该像这样比较浮点数:

|x - y| < ε, ε very small

如果右值不为0.0,比如0.2323121212,我认为你的答案更好。 - fangzhzh

2

d.equals(0) : 0 是一个 int。只有 Double.equals() 代码可以返回 true,对于 Double 对象。


0 是一个 int,将会自动装箱为 Integer - Joachim Sauer
@Joachim Sauer 那都是假设 :) - Suraj Chandran
2
但显然OP并没有理解。详细说明这些事情是个好主意。 - Stephen C

2
当你执行时
d == 0

这被向上转型为

d == 0.0

然而,自动装箱没有向上转型规则,即使有这样的规则,equals(Object)也无法确定您需要一个Double而不是一个Integer。


3
实际上,在“==”上它根本没有被限制,它只是被转换了,这就是我一直在寻找的答案。 - Simeon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接