2010-09-30 7 views
16

J'utilise un findbugs dans un script ANT et je n'arrive pas à résoudre deux de mes erreurs. J'ai lu la documentation, mais je ne comprends pas. Voici mes erreurs et le code qui les accompagne:Test d'égalité à virgule flottante. (FE_FLOATING_POINT_EQUALITY)

Erreur 1: test d'égalité à virgule flottante. (FE_FLOATING_POINT_EQUALITY)

private boolean equals(final Quantity other) { 
    return this.mAmount == convertedAmount(other); 
} 

Erreur 2: EQ_COMPARETO_USE_OBJECT_EQUALS

public final int compareTo(final Object other) { 
    return this.description().compareTo(((Decision) other).description()); 
} 

J'ai lu la documentation de la question de ComparesTo qui indique

Il est fortement recommandé, mais pas strictement nécessaire que (x.compareTo (y) == 0) == (x.équals (y)). D'une manière générale, toute classe qui implémente l'interface Comparable et viole cette condition devrait clairement indiquer ce fait. La langue recommandée est "Note: cette classe a un ordre naturel qui est incompatible avec des égaux."

et aussi la documentation en ce qui concerne le point flottant égalité

Cette opération compare deux valeurs à virgule flottante pour l'égalité. Étant donné que les calculs en virgule flottante peuvent impliquer un arrondi, les valeurs flottantes calculées et les valeurs doubles peuvent ne pas être précises. Pour les valeurs qui doivent être précises, telles que les valeurs monétaires, pensez à utiliser un type de précision fixe tel que BigDecimal. Pour les valeurs qui n'ont pas besoin d'être précises, pensez à comparer l'égalité dans une certaine plage, par exemple: if (Math.abs (x - y) < .0000001). Voir la spécification du langage Java, section 4.2.4.

Je ne comprends pas. quelqu'un peut-il aider s'il vous plait?

Répondre

15

Problème 1:

Pour la question de FE_FLOATING_POINT_EQUALITY, vous ne devriez pas comparer deux valeurs flottantes directement avec l'opérateur ==, car en raison d'erreurs minuscules d'arrondi, les valeurs peuvent être sémantiquement « égale » pour votre application, même si la condition value1 == value2 n'est pas vraie.

Pour résoudre ce problème, modifiez votre code comme suit:

private boolean equals(final Quantity other) { 
    return (Math.abs(this.mAmount - convertedAmount(other)) < EPSILON); 
} 

Où EPSILON est une constante que vous devez définir dans votre code, et représente de petites différences qui sont acceptables pour votre application, par exemple .0000001.

Problème 2:

Pour la question EQ_COMPARETO_USE_OBJECT_EQUALS: Il est fortement recommandé que partout où x.compareTo(y) retourne zéro, x.equals(y) devrait être true. Dans votre code, vous avez implémenté compareTo, mais vous n'avez pas redéfini equals, donc vous héritez de l'implémentation de equals de Object, et la condition ci-dessus n'est pas remplie.

Pour résoudre ce problème, passer outre equals (et peut-être hashCode) dans votre classe, de sorte que lorsque x.compareTo(y) retourne 0, alors x.equals(y) retourneront true.

+3

Une méthode 'equals' doit être transitive (http://javarevisited.blogspot.fr/2011/02/ how-to-write-equals-method-in-java.html) et cohérent avec la méthode 'hashCode'. Dites-nous en plus sur votre implémentation 'equals' comme' (Math.abs (this.mAmount - convertedAmount (other))

+1

@PascalCuoq avez-vous résolu cela? Une façon dont je peux penser est de convertir float en BigDecimal, rond à la précision dont vous avez besoin et calculer le hachage pour cela. Mais il y aura un impact sur les performances. –

+1

@AlexanderMalakhov Je ne suis pas la personne qui a posé la question. J'ai seulement fait un commentaire sur la pertinence de définir une méthode «égale» non-transitive. Il n'y a pas de problème à "résoudre" ici. Les valeurs à virgule flottante peuvent être hachées, et peuvent être comparées pour l'égalité avec les opérateurs de base que Java offre déjà. Toute personne qui définit une méthode 'equals' non transitive crée un problème pour elle-même, et le moyen le plus simple de résoudre le problème est de ne pas le créer en premier lieu. –

6

Pour l'avertissement de virgule flottante, vous devez garder à l'esprit que les flotteurs sont un type inexact. Une référence standard donnée à ce sujet (qui vaut la peine d'être lue une fois peut-être) est la suivante:

What Every Computer Scientist Should Know About Floating-Point Arithmetic par David Goldberg. Parce que les flotteurs ne sont pas des valeurs exactes - même s'ils ressemblent le même lorsqu'il est arrondi à quelques décimales - ils peuvent différer très légèrement, et ne correspondent pas.

Le Comparable interface attend un certain comportement de son implémenteur; l'avertissement vous dit que vous ne respectez pas cela et propose des actions suggérées.

1

Je ne suis pas d'accord avec les réponses ci-dessus. Est égal à et compareTo sont le mauvais endroit pour introduire epsilons dans les comparaisons à virgule flottante.

Les valeurs de virgule flottante peuvent être comparées exactement par des équations et comparez à, en utilisant simplement l'opérateur "==".
Si votre application, utilise des flottants résultat du calcul, besoin de comparer ces valeurs avec l'approche epsilon, il devrait le faire uniquement à l'endroit où cela est nécessaire. Par exemple, une méthode d'intersection de lignes mathématiques.
Mais pas en égal et comparez à.

Cet avertissement est très trompeur. Cela signifie comparer deux flotteurs où au moins un résultat est un résultat inattendu. Cependant, souvent ces flotteurs, de comparer, ne sont pas le résultat d'un calcul, comme

static final double INVALID_VALUE = -99.0; 
if (f == INVALID_VALUE) 

où f est initialisé avec INVALID_VALUE, sera en Java fonctionne toujours parfaitement. Mais findbugs et sonarcube vont encore se plaindre.

Donc, il suffit d'ajouter un filtre à ignorer findbugs, Asuming vous avez deux classes MyPoint2D et Myrectangle2D

<Match> 
     <OR> 
      <Class name="~.*\.MyPoint2D" /> 
      <Class name="~.*\.MyRectangle2D" /> 
     </OR> 
     <Bug code="FE" /> 
     <Justification author="My Name" /> 
     <Justification 
      text="Floating point equals works (here)." /> 
    </Match> 
+0

L'utilisation d'une "valeur invalide" n'est pas une approche propre en premier lieu. Mais l'optimisation (souvent prématurée) peut conduire à un tel piratage et peut même être la meilleure solution à la fin. Du point de vue du code propre, vous devriez plutôt utiliser un champ séparé contenant les informations, que le flotteur soit valide ou non. Ensuite, vous n'auriez pas besoin de penser à vérifier les flotteurs pour l'égalité. – Alfe

+0

Je suis d'accord, dans de nombreuses situations, il est préférable d'avoir un champ séparé pour montrer la validité. Dans de nombreuses autres situations, non. (code interne non public à toute autre classe) Le sujet principal ici est que la comparaison avec les constantes de point flottant fonctionne – AlexWien

+0

La notion que le code interne peut être moins propre est dangereuse. Le code interne peut également nécessiter une maintenance par un développeur successeur qui doit comprendre le code. Mais je vais accepter l'argument "hautement optimisé" dans certains cas. – Alfe

Questions connexes