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

CSnerd
  • 2,129
  • 8
  • 22
  • 45

3 Answers3

4

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. doubles, 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.

resueman
  • 10,572
  • 10
  • 31
  • 45
2

-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.

Community
  • 1
  • 1
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
1

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.

thst
  • 4,592
  • 1
  • 26
  • 40