Por que o Double.compare (double, double) do Java é implementado da maneira que é?

Eu estava olhando para a implementação de compare (double, double) na biblioteca padrão Java (6). Lê:

public static int compare(double d1, double d2) { 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) } 

Quais são os méritos desta implementação?


edit: “Méritos” foi uma (muito) má escolha de palavras. Eu queria saber como isso funciona.

A resposta de @ Shoover está correta, mas há um pouco mais do que isso.

Como o javadoc para Double::equals states:

“Esta definição permite que as tabelas de hash funcionem corretamente.”

Suponha que os projetistas Java tenham decidido implementar equals(...) e compare(...) com a mesma semântica de == nas instâncias double encapsuladas. Isso significaria que equals() sempre retornaria false para um NaN empacotado. Agora, considere o que aconteceria se você tentasse usar um NaN em um Mapa ou Coleção.

 List l = new ArrayList(); l.add(Double.NaN); if (l.contains(Double.NaN)) { // this wont be executed. } Map m = new HashMap(); m.put(Double.NaN, "Hi mum"); if (m.get(Double.NaN) != null) { // this wont be executed. } 

Não faz muito sentido faz isso!

Outras anomalias existiriam porque -0.0 e -0.0 possuem padrões de bits diferentes, mas são iguais de acordo com == .

Assim, os projetistas de Java decidiram (com razão, IMO) a definição mais complicada (mas mais intuitiva) para esses methods Double que temos hoje.

A explicação está nos comentários no código. Java tem valores duplos para 0.0 e -0.0 , bem como “não um número” ( NaN ). Você não pode usar o operador simples == para esses valores. Dê uma olhada na fonte doubleToLongBits() e no Javadoc para o método Double.equals() :

Note que na maioria dos casos, para duas instâncias da class Double , d1 e d2 , o valor de d1.equals(d2) é true se e somente se

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

também tem o valor true . No entanto, existem duas exceções:

  • Se d1 e d2 representam Double.NaN , o método equals retorna true , embora Double.NaN == Double.NaN tenha o valor false .
  • Se d1 representa +0.0 enquanto d2 representa -0.0 , ou vice-versa, o teste de igual tem o valor false , mesmo que +0.0 == -0.0 tenha o valor true .

Essa definição permite que as tabelas de hash funcionem corretamente.

O mérito é que é o código mais simples que preenche a especificação.

Uma característica comum dos programadores novatos é supervalorizar as especificações do código-fonte de leitura e da leitura de subvalor. Neste caso, a especificação:

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

… torna o comportamento e a razão para o comportamento (consistência com igual ()) perfeitamente clara.

Essa implementação permite que um número real seja definido como