views:

916

answers:

5

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?

+3  A: 

I think the main merit is that it's correct.

It correctly handles NaN and signed zeroes.

erickson
Apologies, my question should have been more descriptive. I was looking for an answer like Stephen's.
Nimnio
A: 

That implementation allows a real number to be defined as < NaN, and -0.0 as < 0.0.

phoebus
+12  A: 

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 Double.equals():

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.
shoover
+4  A: 

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

Just 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!

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

Stephen C
Thanks, Stephen. I'm marking your answer correct instead of shoover's because I wanted to know why the Java designers implemented compare the way they did (i.e. "the merits"), not just what the code did (although I didn't know that either). My apologies to shoover for not being more definite, but thanks to you both.
Nimnio
Due to floating point accuracy issues, I would expect nothing but problems from code using them as lookup keys.
280Z28
@280228 - well yes, the keys >>could<< be generated in a way that is insensitive to rounding errors, etc. The point is that if someone did build an application that avoided the problem of floating point errors, they would expect the Collection types to work.
Stephen C
+2  A: 

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.

Kevin Bourrillion
Hmm, you're right, I should have read that closer, but you shouldn't assume I undervalue the specifications. I started with the Javadoc for compare(...), then moved on to doubleToLongBits(...), and then checked the Wikipedia article on IEEE 754, by which time I had forgotten all about the earlier mention of compareTo(...). So, I think you'll have to agree, overvaluing the source code isn't my problem, but inefficiently reading the specifications is!p.s. Your link is broken.
Nimnio