Почему 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.