2009-11-12 14 views
33

Estaba viendo la implementación de compare(double, double) en la biblioteca estándar de Java (6). Se lee:¿Por qué Java's Double.compare (doble, doble) está implementado como está?

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

¿Cuáles son los méritos de esta implementación?


corregir: "Merits" fue una (muy) mala elección de palabras. Quería saber cómo funciona esto.

+1

Relacionado con esto y toda la discusión aquí: echa un vistazo a este poco de horribleidad JDK en el área de la comparación de punto flotante: http://publicobject.com/2009/11/floating-point-equality.html –

+0

@KevinBourrillion - La forma en que lo leí, en realidad se trata de la horribleidad del estándar de coma flotante IEE. Java tiene que comportarse de acuerdo con el estándar IEE, porque eso es lo que implementa el hardware en una máquina moderna. –

Respuesta

38

@ respuesta de Shoover es correcta, pero hay un poco más que esto.

Como los javadoc para Double::equals estados:

"Esta definición permite tablas hash para operar correctamente."

Supongamos que los diseñadores de Java habían decidido implementar equals(...) y compare(...) con la misma semántica que == en los casos double envueltos. Esto significa que equals() siempre devolvería false para un NaN envuelto.Ahora considere lo que sucedería si tratara de usar un NaN envuelto en un Mapa o Colección.

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

¡No tiene mucho sentido!

Otras anomalías existirían porque -0.0 y +0.0 tienen patrones de bits diferentes pero son iguales según ==.

Así que los diseñadores de Java decidieron (con razón IMO) en la definición más complicada (pero más intuitiva) para estos métodos dobles que tenemos hoy.

+0

Gracias, Stephen. Estoy marcando tu respuesta correcta en lugar de la de shoover porque quería saber por qué los diseñadores de Java implementados se comparan de la manera en que lo hicieron (es decir, "los méritos"), no solo lo que hizo el código (aunque yo tampoco lo sabía). Mis disculpas por no ser más definido, pero gracias a ustedes dos. – DavidS

+0

Debido a problemas de precisión de punto flotante, no esperaría nada más que problemas del código al usarlos como claves de búsqueda. –

+0

@ 280228 - bueno sí, las claves >> podrían << ser generadas de una manera que no es sensible a los errores de redondeo, etc. El punto es que si alguien construyera una aplicación que evitara el problema de los errores de punto flotante, esperarían los tipos de Colección para trabajar. –

5

Creo que el mérito principal es que es correcto.

Maneja correctamente NaN y firmó ceros.

+1

Disculpas, mi pregunta debería haber sido más descriptiva. Estaba buscando una respuesta como la de Stephen. – DavidS

0

Esa implementación permite que un número real se defina como < NaN, y -0.0 como < 0.0.

43

La explicación está en los comentarios del código. Java tiene valores dobles para 0.0 y -0.0, así como "no es un número" (NaN). No puede usar el operador simple == para estos valores. Echar un vistazo a la fuente de doubleToLongBits() y al the Javadoc for the Double.equals() method:

Tenga en cuenta que en la mayoría de los casos, para los dos instancias de la clase Double, d1 y d2, el valor de d1.equals(d2) es true si y sólo si

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

también tiene el valor true. Sin embargo, hay dos excepciones:

  • Si d1 y d2 ambos representan Double.NaN, entonces el método es igual regresos true, incluso aunque Double.NaN == Double.NaN tiene el valor false.
  • Si d1 representa +0.0 mientras d2 representa -0.0, o viceversa, la prueba de igualdad tiene el valor false, a pesar de que +0.0 == -0.0 tiene el valor true.
2

El mérito es que es el código más simple que cumple con la especificación.

Una característica común de los programadores novatos es sobrevalorar el código fuente de lectura y infravalorar la lectura especificaciones. En este caso, la especificación:

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

... hace que el comportamiento y la razón para el comportamiento (coherencia con los iguales()) perfectamente claras.

+0

Hmm, tienes razón, debería haberlo leído más de cerca, pero no debes asumir que infravalore las especificaciones. Comencé con el Javadoc para comparar (...), luego pasé a doubleToLongBits (...) y luego revisé el artículo de Wikipedia sobre IEEE 754, momento en el que me olvidé por completo de la mención anterior de compareTo (... .). Por lo tanto, creo que tendrá que estar de acuerdo, sobrevalorar el código fuente no es mi problema, ¡pero leer ineficazmente las especificaciones sí lo es! p.s. Tu enlace está roto. – DavidS

Cuestiones relacionadas