38 votes

Pourquoi la fonction Double.compare(double, double) de Java est-elle implémentée de cette manière ?

Je regardais l'implémentation de compare(double, double) dans la bibliothèque standard de Java (6). Il se lit comme suit :

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

Quels sont les mérites de cette mise en œuvre ?


edit : "Merits" était un (très) mauvais choix de mots. Je voulais savoir comment cela fonctionne.

1 votes

Dans le même ordre d'idées que la discussion qui se tient ici, jetez un coup d'œil à cette horreur du JDK dans le domaine de la comparaison des nombres à virgule flottante : publicobject.com/2009/11/floating-point-equality.html

0 votes

@KevinBourrillion - D'après ce que j'ai lu, il s'agit en fait de l'horreur de la norme de virgule flottante de l'IEE. Java doit se comporter conformément à la norme IEE, car c'est ce que le matériel d'une machine moderne met en œuvre.

46voto

shoover Points 1631

L'explication se trouve dans les commentaires du code. Java a des valeurs doubles pour les deux 0.0 y -0.0 ainsi que "pas un numéro" ( NaN ). Vous ne pouvez pas utiliser de simples == pour ces valeurs. Jetez un coup d'œil à la doubleToLongBits() et à la Javadoc pour le Double.equals() méthode :

Notez que dans la plupart des cas, pour deux instances de la classe Double , d1 y d2 , la valeur de d1.equals(d2) est true si et seulement si

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

a également la valeur true . Cependant, il y a deux exceptions :

  • Si d1 y d2 les deux représentent Double.NaN alors la méthode equals renvoie true même bien que Double.NaN == Double.NaN a la valeur false .
  • Si d1 représente +0.0 tandis que d2 représente -0.0 ou vice versa, le test d'égalité a pour valeur false même si +0.0 == -0.0 a la valeur true .

Cette définition permet aux tables de hachage de fonctionner correctement.

43voto

Stephen C Points 255558

La réponse de @Shoover est correcte ( Lisez-le ! ), mais il y a un peu plus que cela.

Comme le javadoc pour Double::equals États :

"Cette définition permet aux tables de hachage de fonctionner correctement."

Supposons que les concepteurs de Java aient décidé d'implémenter equals(...) y compare(...) avec la même sémantique que == sur l'emballage double instances. Cela signifie que equals() retournerait toujours false pour un NaN enveloppé. Imaginez maintenant ce qui se passerait si vous essayiez d'utiliser un NaN enveloppé dans une carte ou une 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.
}

Ça n'a pas beaucoup de sens, n'est-ce pas ?

D'autres anomalies existeraient parce que -0.0 y +0.0 ont des configurations binaires différentes mais sont égales selon == .

Les concepteurs de Java ont donc opté (à juste titre, selon moi) pour la définition plus compliquée (mais plus intuitive) de ces méthodes doubles que nous avons aujourd'hui.

0 votes

Merci, Stephen. Je marque votre réponse correcte au lieu de celle de shoover parce que je voulais savoir pourquoi les concepteurs de Java ont implémenté la comparaison de la manière dont ils l'ont fait (c'est-à-dire "les mérites"), et pas seulement ce que le code faisait (bien que je ne le sache pas non plus). Mes excuses à shoover pour ne pas avoir été plus précis, mais merci à vous deux.

0 votes

En raison des problèmes de précision des virgules flottantes, je ne m'attends à rien d'autre qu'à des problèmes dans le code qui les utilise comme clés de recherche.

0 votes

@280228 - bien oui, les clés >>pourraient<< être générées d'une manière insensible aux erreurs d'arrondis, etc. Le fait est que si quelqu'un construisait une application qui évite le problème des erreurs de virgule flottante, il s'attendrait à ce que les types de collection fonctionnent.

5voto

erickson Points 127945

Je pense que le principal mérite est que c'est correct.

Il gère correctement les NaN et les zéros signés.

2voto

Kevin Bourrillion Points 19677

Le mérite est que c'est le code le plus simple qui remplit la spécification.

Une caractéristique commune aux programmeurs débutants est de surévaluer la lecture du code source et de sous-évaluer la lecture spécifications . Dans ce cas, le spec :

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

... rend le comportement et le la raison du comportement (cohérence avec equals()) parfaitement claire.

0 votes

Hmm, vous avez raison, j'aurais dû lire cela de plus près, mais vous ne devriez pas supposer que je sous-estime les spécifications. J'ai commencé par la Javadoc pour compare(...), puis je suis passé à doubleToLongBits(...), puis j'ai vérifié l'article de Wikipedia sur IEEE 754, et j'avais alors oublié la mention précédente de compareTo(...). Donc, je pense que vous serez d'accord, la surévaluation du code source n'est pas mon problème, mais la lecture inefficace des spécifications l'est ! p.s. Votre lien est cassé.

-1voto

phoebus Points 7358

Cette implémentation permet de définir un nombre réel comme < NaN, et -0,0 comme < 0,0.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X