简体   繁体   中英

Why HashSet differentiates 0.0 and -0.0

When I tried this:

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));

The output was what I expected:

true
true

But when I tried:

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));

or

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

To my surprise, the output was:

true
false

Why this happened? How do I make HashSet treat (0.0) and (-0.0) the same? Is there a better way than if(num == -0.0) num = 0.0; ?

This is explained by the docs for 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.

So a Double created from 0.0 is not the same as a Double created from -0.0 . The same is not true when you use 0 and -0 because integers use twos-complement, which has no notion of negative zero. -0 is the same as 0 . double s, on the other hand, use the IEEE standard for floating point values , which does recognize a negative zero value.

This behavior is all fixed, so there's no way to have a HashSet treat 0.0 and -0.0 as the same. If you want to do that, you'll need to manually convert all negative zero values into positive zeros before adding or searching for them.

-0.0 is a literal for a double value that is distinct from 0.0 .

-0 is the negation operator applied to the int value 0 , which gives just the int value 0 .

Therefore, new Double(-0) is equivalent to new Double(0) , whereas new Double(-0.0) and new Double(0.0) actually produce two non-equal Double objects.

See this question for some explanations for why it is necessary to have two different floating point zero values.

As explained in Wikipedia (thanks), the IEEE format for floating point actually supports negative and positive zeros for various reasons. https://en.wikipedia.org/wiki/Signed_zero

The reason why you find the 0.0 and -0.0 different in your hashmap is directly derived from the IEEE representation. The Double#hashCode method uses the raw bits of the floating point number to calculate the hashcode. Since 0.0 and -0.0 and possibly even +0.0 are different in terms of bits, as some numerical calculations obviously require this, their bits are different and therefore the hashcodes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM