Почему Java Double.compare(двойная, двойная) реализована так, как она есть?

Я смотрел на реализацию сравнить (double, double) в стандартной библиотеке Java (6). Он гласит:

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

Каковы достоинства этой реализации?


edit: "Merits" был (очень) плохим выбором слов. Я хотел знать, как это работает.

Ответ 1

@Правильный ответ правильный, но есть немного больше, чем это.

В качестве javadoc для Double::equals указано:

"Это определение позволяет хэш-таблицам работать правильно.

Предположим, что разработчики Java решили реализовать equals(...) и compare(...) с той же семантикой, что и == в обернутых экземплярах double. Это означало бы, что equals() всегда возвращал false для завернутого NaN. Теперь рассмотрим, что произойдет, если вы попытаетесь использовать завернутый NaN в Map или 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.
}

Не имеет большого значения это делает!

Другие аномалии будут существовать, потому что -0.0 и +0.0 имеют разные битовые шаблоны, но равны в соответствии с ==.

Итак, разработчики Java решили (правильно ИМО) более сложное (но более интуитивное) определение для этих двойных методов, которые мы имеем сегодня.

Ответ 2

Объяснение приведено в комментариях в коде. Java имеет двойные значения для 0.0 и -0.0, а также "не число" (NaN). Вы не можете использовать простой оператор == для этих значений. Загляните в источник doubleToLongBits() и Javadoc для метода Double.equals():

Обратите внимание, что в большинстве случаев для двух экземпляры класса Double, d1 и d2, значение d1.equals(d2) равно true, если и только если

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

также имеет значение true. Однако, есть два исключения:

  • Если d1 и d2 оба представляют Double.NaN, то метод equals возвращает true, даже хотя Double.NaN == Double.NaN имеет значение false.
  • Если d1 представляет +0.0, а d2 представляет -0.0, или наоборот, равный тест имеет значение false, хотя +0.0 == -0.0 имеет значение true.

Ответ 3

Я считаю, что главная заслуга в том, что это правильно.

Он правильно обрабатывает NaN и подписанные нули.

Ответ 4

Достоинством является то, что это самый простой код, который выполняет спецификацию.

Одной из характерных черт для новичков-программистов является переоценка исходного кода чтения и недооцененных значений чтения. В этом случае спецификация:

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

... делает поведение и причину поведения (согласованность с equals()) совершенно ясным.

Ответ 5

Эта реализация позволяет определить действительное число как < NaN и -0,0, как < 0.0.