从this question我学到了Double.NaN与自己不相同。
我正在为自己验证这一点,并注意到如果你在Double实例中包装Double.NaN就不是这种情况。例如:
public class DoubleNaNTest {
public static void main(String[] args) {
double primitive = Double.NaN;
Double object = new Double(primitive);
// test 1 - is the primitive is equal to itself?
boolean test1 = primitive == primitive;
// test 2 - is the object equal to itself?
boolean test2 = object.equals(object);
// test 3 - is the double value of the object equal to itself?
boolean test3 = object.doubleValue() == object.doubleValue();
System.out.println("Test 1 = " + test1);
System.out.println("Test 2 = " + test2);
System.out.println("Test 3 = " + test3);
}
}
输出:
Test 1 = false
Test 2 = true
Test 3 = false
在我看来,所有三个测试都应该评估为false,因为所有三个操作都是等效的(如果你使用Double.NaN之外的其他东西就是这样)。
有人能解释一下这里发生了什么吗?
答案 0 :(得分:11)
发生的事情是equals
方法故意偏离IEE浮点。从Javadoc引用java.lang.Double的equals(Object)
方法。
但是,有两个例外:
- 如果d1和d2都代表Double.NaN,那么等于方法 即使返回true也是如此 Double.NaN == Double.NaN具有该值 假的。
- 如果d1代表+0.0而d2代表-0.0,反之亦然 等值测试的值为false,即使是 虽然+0.0 == - 0.0的值为真。
此定义允许哈希表 运作正常。
结果是,如果您想要100%IEE浮点兼容性,则需要明确取消java.lang.Double
个实例的装箱并比较生成的double
值。
答案 1 :(得分:5)
他们故意偏离IEEE方式,因此散列表可以正常工作。
你可以获得故事的一部分in the api docs。故事的其余部分是:每个继承自Object的类(都是它们)都有一个维护Object不变量的契约。合同存在,以便库的其余部分可以实现所有那些好的集合和事物。没有什么可以阻止你破坏它,但是你不能确定使用Object的东西会起作用。
答案 2 :(得分:0)
如果有两个不可变对象定义Equals
,当一个对象中的每个字段将自己Equal
报告给另一个对应的字段时,如果没有使用该对象的代码,则返回true关心引用相等性,那么应该可以用对另一个对象的引用替换对一个对象的所有引用。如果对象是由例如生成的。解析从磁盘读取的数据,如果许多对象比较相等,则用一个实例的引用替换许多不同的实例可以大大提高性能。请注意,这种替换的正确性并不取决于对象是浅层还是深层不可变的,前提是嵌套在其中的任何可变对象都会将自身报告为与其他任何东西不相等。
为使这种逻辑正常工作,重要的是所讨论的对象完全等效。每个对象都必须与自身相等,因此Double.NaN
必须等于Double.NaN
。完全等价的要求意味着正零必须不报告自己等于负零(因为如果确实如此,保持正零的对象可能与保持负零的对象的行为不同)。
注意,顺便说一下,这是.net与Java不同的区域(一些非等价的浮点和Decimal
值是报告Equals
);在很多方面,我认为.net是优越的,但这个细节是.net出错了。