Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is Java's Double.compare(double, double) implemented the way it is?

I was looking at the implementation of compare(double, double) in the Java standard library (6). It reads:

public static int compare(double d1, double d2) {
    if (d1 < d2)
        return -1;       // Neither val is NaN, thisVal is smaller
    if (d1 > d2)
        return 1;        // Neither val is NaN, thisVal is larger

    long thisBits = Double.doubleToLongBits(d1);
    long anotherBits = Double.doubleToLongBits(d2);

    return (thisBits == anotherBits ?  0 : // Values are equal
            (thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
             1));                          // (0.0, -0.0) or (NaN, !NaN)
}

What are the merits of this implementation?


edit: "Merits" was a (very) bad choice of words. I wanted to know how this works.

like image 232
DavidS Avatar asked Nov 12 '09 23:11

DavidS


3 Answers

The explanation is in the comments in the code. Java has double values for both 0.0 and -0.0, as well as "not a number" (NaN). You can't use simple == operator for these values. Take a peek into the doubleToLongBits() source and at the Javadoc for the Double.equals() method:

Note that in most cases, for two instances of class Double, d1 and d2, the value of d1.equals(d2) is true if and only if

d1.doubleValue() == d2.doubleValue()

also has the value true. However, there are two exceptions:

  • If d1 and d2 both represent Double.NaN, then the equals method returns true, even though Double.NaN == Double.NaN has the value false.
  • 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.

This definition allows hash tables to operate properly.

like image 61
shoover Avatar answered Nov 15 '22 03:11

shoover


@Shoover's answer is correct (read it!), but there is a bit more to it than this.

As the javadoc for Double::equals states:

"This definition allows hash tables to operate properly."

Suppose that the Java designers had decided to implement equals(...) and compare(...) with the same semantics as == on the wrapped double instances. This would mean that equals() would always return false for a wrapped NaN. Now consider what would happen if you tried to use a wrapped NaN in a Map or Collection.

List<Double> l = new ArrayList<Double>();
l.add(Double.NaN);
if (l.contains(Double.NaN)) {
    // this wont be executed.
}

Map<Object,String> m = new HashMap<Object,String>();
m.put(Double.NaN, "Hi mum");
if (m.get(Double.NaN) != null) {
    // this wont be executed.
}

Doesn't make a lot of sense does it!

Other anomalies would exist because -0.0 and +0.0 have different bit patterns but are equal according to ==.

So the Java designers decided (rightly IMO) on the more complicated (but more intuitive) definition for these Double methods that we have today.

like image 37
Stephen C Avatar answered Nov 15 '22 03:11

Stephen C


The merit is that it's the simplest code that fulfills the specification.

One common characteristic of rookie programmers is to overvalue reading source code and undervalue reading specifications. In this case, the spec:

http://java.sun.com/javase/6/docs/api/java/lang/Double.html#compareTo%28java.lang.Double%29

... makes the behavior and the reason for the behavior (consistency with equals()) perfectly clear.

like image 2
Kevin Bourrillion Avatar answered Nov 15 '22 02:11

Kevin Bourrillion