为什么 HashSet 区分 0.0 和 -0.0

Why HashSet differentiates 0.0 and -0.0

当我尝试这个时:

HashSet<Double> set = new HashSet<>();
Double d1 = new Double(0);
Double d2 = new Double(0);
Double d3 = new Double(-0);
set.add(d1);
System.out.println(set.contains(d2));
System.out.println(set.contains(d3));

输出符合我的预期:

true
true

但是当我尝试时:

HashSet<Double> set = new HashSet<>();
Double d1 = new Double(0.0);
Double d2 = new Double(0.0);
Double d3 = new Double(-0.0);
set.add(d1);
System.out.println(set.contains(d2));
System.out.println(set.contains(d3));

set.add(Double.valueOf(d1));
System.out.println(set.contains(Double.valueOf(d2)));
System.out.println(set.contains(Double.valueOf(d3)));

令我惊讶的是,输出是:

true
false

为什么会这样?我如何使 HashSet 对待 (0.0) 和 (-0.0) 相同? 有没有比if(num == -0.0) num = 0.0;更好的方法?

-0.0double 值的文字,不同于 0.0.

-0 是应用于 int0 的否定运算符,它仅给出 int0.

因此,new Double(-0)等价于new Double(0),而new Double(-0.0)new Double(0.0)实际上产生了两个不相等的Double对象。

有关为什么必须有两个不同的浮点零值的一些解释,请参阅 this question

这由 the docsDouble 解释。

If d1 represents +0.0 while d2 represents -0.0, or vice versa, the equal test has the value false, even though +0.0==-0.0 has the value true.

因此从 0.0 创建的 Double 与从 -0.0 创建的 Double 不同 。当您使用 0-0 时情况并非如此,因为整数使用二进制补码,它没有负零的概念。 -00 相同。 doubles,另一方面,use the IEEE standard for floating point values,它确实识别负零值。

此行为已全部修复,因此无法让 HashSet0.0-0.0 视为相同。如果您想这样做,您需要在添加或搜索它们之前手动将所有负零值转换为正零值。

如维基百科中所述(感谢),由于各种原因,浮点的 IEEE 格式实际上支持负零和正零。 https://en.wikipedia.org/wiki/Signed_zero

您在哈希图中发现 0.0 和 -0.0 不同的原因直接来自 IEEE 表示。 Double#hashCode 方法使用浮点数的原始位来计算哈希码。由于 0.0 和 -0.0 甚至可能 +0.0 在位方面是不同的,因为某些数值计算显然需要这一点,因此它们的位不同,因此哈希码也不同。