为什么 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.0
是 double
值的文字,不同于 0.0
.
-0
是应用于 int
值 0
的否定运算符,它仅给出 int
值 0
.
因此,new Double(-0)
等价于new Double(0)
,而new Double(-0.0)
和new Double(0.0)
实际上产生了两个不相等的Double
对象。
有关为什么必须有两个不同的浮点零值的一些解释,请参阅 this question。
这由 the docs 为 Double
解释。
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
时情况并非如此,因为整数使用二进制补码,它没有负零的概念。 -0
与 0
相同。 double
s,另一方面,use the IEEE standard for floating point values,它确实识别负零值。
此行为已全部修复,因此无法让 HashSet
将 0.0
和 -0.0
视为相同。如果您想这样做,您需要在添加或搜索它们之前手动将所有负零值转换为正零值。
如维基百科中所述(感谢),由于各种原因,浮点的 IEEE 格式实际上支持负零和正零。 https://en.wikipedia.org/wiki/Signed_zero
您在哈希图中发现 0.0 和 -0.0 不同的原因直接来自 IEEE 表示。 Double#hashCode 方法使用浮点数的原始位来计算哈希码。由于 0.0 和 -0.0 甚至可能 +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.0
是 double
值的文字,不同于 0.0
.
-0
是应用于 int
值 0
的否定运算符,它仅给出 int
值 0
.
因此,new Double(-0)
等价于new Double(0)
,而new Double(-0.0)
和new Double(0.0)
实际上产生了两个不相等的Double
对象。
有关为什么必须有两个不同的浮点零值的一些解释,请参阅 this question。
这由 the docs 为 Double
解释。
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
时情况并非如此,因为整数使用二进制补码,它没有负零的概念。 -0
与 0
相同。 double
s,另一方面,use the IEEE standard for floating point values,它确实识别负零值。
此行为已全部修复,因此无法让 HashSet
将 0.0
和 -0.0
视为相同。如果您想这样做,您需要在添加或搜索它们之前手动将所有负零值转换为正零值。
如维基百科中所述(感谢),由于各种原因,浮点的 IEEE 格式实际上支持负零和正零。 https://en.wikipedia.org/wiki/Signed_zero
您在哈希图中发现 0.0 和 -0.0 不同的原因直接来自 IEEE 表示。 Double#hashCode 方法使用浮点数的原始位来计算哈希码。由于 0.0 和 -0.0 甚至可能 +0.0 在位方面是不同的,因为某些数值计算显然需要这一点,因此它们的位不同,因此哈希码也不同。