web-dev-qa-db-fra.com

Comparaison de nombres dans Java

En Java, tous les types numériques s'étendent de Java.lang.Number. Serait-ce une bonne idée d'avoir une méthode comme la suivante:

public boolean areEqual(Number first, Number second) {
    if (first != null && second != null) {
        return first.equals(second);
    }
}

Je m'inquiète des cas où un double de 2,00000 n'est pas égal à un entier 2. Sont-ils traités par les égaux intégrés? Sinon, existe-t-il un moyen d'écrire une simple fonction de comparaison de nombres en java? (les bibliothèques externes telles que Apache commons sont correctes)

20
David

Un Double est [~ # ~] jamais [~ # ~]equals vers un Integer. De plus, un double n'est pas la même chose qu'un Double.

Java a des types primitifs et des types de référence. Les types véritablement numériques de Java ne s'étendent pas de Number, car ce sont des primitives.

Vous voudrez peut-être envisager un système où vous ne mélangez pas les types, car cela causera généralement beaucoup de problèmes avec les conversions implicites/explicites qui peuvent/peuvent ne pas perdre d'informations, etc.

Questions connexes

Sur int vs Integer:

Sur Number comparaison:

Voir également


Sur le calcul de type mixte

Le calcul de type mixte fait l'objet d'au moins 4 puzzles en Java Puzzlers .

Voici différents extraits:

il est généralement préférable d'éviter les calculs de type mixte [...] car ils sont intrinsèquement déroutants [...] Nulle part cela n'est plus apparent que dans les expressions conditionnelles. Les comparaisons de types mixtes sont toujours source de confusion car le système est obligé de promouvoir un opérande pour qu'il corresponde au type de l'autre. La conversion est invisible et peut ne pas produire les résultats attendus

Prescription : évitez les calculs qui mélangent les types intégraux et à virgule flottante. Préférez l'arithmétique intégrale à virgule flottante.

41
polygenelubricants

Je sais que c'est un vieux sujet, mais .... Pour comparer deux nombres dans Java vous pouvez utiliser la méthode compareTo de BigDecimal. BigDecimal peut tout contenir de court à double ou BigInteger, donc c'est le classe parfaite pour cela.

Vous pouvez donc essayer d'écrire quelque chose comme ceci:

public int compareTo(Number n1, Number n2) {
    // ignoring null handling
    BigDecimal b1 = new BigDecimal(n1.doubleValue());
    BigDecimal b2 = new BigDecimal(n2.doubleValue());
    return b1.compareTo(b2);
}

Ce n'est certainement pas la meilleure approche en matière de performances. Les tests suivants ont fonctionné jusqu'à présent, au moins avec JDK7:

assertTrue(compareTo(new Integer(1), new Integer(2)) == -1);
assertTrue(compareTo(new Integer(1), new Double(2.0)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MAX_VALUE)) == -1);
assertTrue(compareTo(new Integer(1), new Double(Double.MIN_VALUE)) == 1);
assertTrue(compareTo(new Integer(1), new Double(1.000001)) == -1);
assertTrue(compareTo(new Integer(1), new Double(1.000)) == 0);
assertTrue(compareTo(new Integer(1), new Double(0.25*4)) == 0);
assertTrue(compareTo(new Integer(1), new AtomicLong(1)) == 0);
11
Lian

La méthode spécifique que vous proposez échouerait, car elle utilise equals() héritée de Object. Autrement dit, il vérifierait si les objets Number étaient les mêmes, pas si leurs les valeurs étaient les mêmes.

Si ce n'était qu'un exemple illustratif, je mettrai à jour ma réponse.

la réponse de polygene couvre en fait à peu près le terrain vers lequel je me dirigeais. Vous pouvez également être intéressé par cette question: Pourquoi Java.lang.Number n'implémente-t-il pas Comparable? .

6
Pops

Si vous voulez savoir si les références d'objet sont les mêmes, les méthodes existantes conviennent à la facture. Un Double représentant 2.0 et un Integer représentant 2 sont certainement des objets différents, et certainement pas interchangeables dans un sens général.

Si vous voulez simplement savoir si les valeurs numériques sont les mêmes, vous pouvez utiliser la méthode Number.doubleValue () pour convertir les deux nombres en doubles, puis comparer ces nombres ensemble (ce qui permet probablement une petite tolérance , car la plupart des nombres sont représentés de manière inexacte, comme 1,99999999996583 pour ce qui devrait être 2, selon les étapes de calcul intermédiaires). Quelque chose comme ceci:

private static final double EPSILON = 0.000000000000001d;    

public static boolean areEquivalentNumbers(Number a, Number b)
{
   if (a == null)
   {
      return b == null;
   }
   else if (b == null)
   {
      return false;
   }
   else
   {
      return Math.abs(a.doubleValue() - b.doubleValue()) < EPSILON;
   }
}
4
Andrzej Doyle

Sur une tangente à quelques-unes des réponses, puis-je suggérer qu'au lieu d'écrire quelque chose comme:

boolean compare(Object o1, Object o2)
{
  if (o1==null)
    return o2==null;
  if (o2==null)
    return false;
  return o1.equals(o2);
}

C'est beaucoup plus concis, et je pense un peu plus efficace, d'écrire:

boolean compare(Object o1, Object o2)
{
  return o1==o2 || o1!=null && o2!=null && o1.equals(o2);
}

Si les deux sont nuls, o1 == o2 retournera vrai. S'ils ne le sont pas mais qu'ils sont le même objet, c'est bien aussi.

Techniquement, le o2! = Null n'est pas nécessaire pour la plupart des implémentations d'égaux, mais si vous étiez vraiment assez générique pour le faire sur les objets comme dans l'exemple ci-dessus, vous ne sauriez bien sûr pas comment chaque remplacement a été écrit.

1
Jay

La comparaison des nombres entre entier et virgule flottante ne donnera presque jamais ce que vous recherchez. Si toutefois il s'agit d'un exercice simple, vous pouvez implémenter la comparaison en comparant les représentations de chaîne des valeurs, comme dans:

public boolean areEqual(Number first, Number second) {
    if (first == null) {
        return second == null;
    }
    if (second == null) {
        return false;
    }

    return first.toString().equals(second.toString());
}
1
rsp