Selon cette page java.sun ==
est l'opérateur de comparaison d'égalité pour les nombres à virgule flottante en Java.
Cependant, lorsque je tape ce code:
if(sectionID == currentSectionID)
dans mon éditeur et exécuter une analyse statique, j'obtiens: "JAVA0078 Valeurs à virgule flottante comparées à =="
Quel est le problème avec l'utilisation ==
pour comparer des valeurs en virgule flottante? Quelle est la bonne façon de procéder?
java
equality
floating-accuracy
user128807
la source
la source
Réponses:
la bonne façon de tester les flottants pour «l'égalité» est:
où epsilon est un très petit nombre comme 0,00000001, en fonction de la précision souhaitée.
la source
if(Math.abs(sectionID - currentSectionID) < epsilon*sectionID
pour résoudre ce problème?Math.ulp()
dans ma réponse à cette question.Les valeurs à virgule flottante peuvent être légèrement décalées, de sorte qu'elles peuvent ne pas être rapportées comme exactement égales. Par exemple, en définissant un flottant sur "6.1" et en l'imprimant à nouveau, vous pouvez obtenir une valeur rapportée de quelque chose comme "6.099999904632568359375". Ceci est fondamental pour le fonctionnement des flotteurs; par conséquent, vous ne voulez pas les comparer en utilisant l'égalité, mais plutôt une comparaison dans une plage, c'est-à-dire si la différence entre le flottant et le nombre auquel vous souhaitez le comparer est inférieure à une certaine valeur absolue.
Cet article sur le registre donne un bon aperçu des raisons pour lesquelles c'est le cas; lecture utile et intéressante.
la source
Juste pour donner la raison de ce que tout le monde dit.
La représentation binaire d'un flotteur est plutôt ennuyeuse.
En binaire, la plupart des programmeurs connaissent la corrélation entre 1b = 1d, 10b = 2d, 100b = 4d, 1000b = 8d
Eh bien, cela fonctionne aussi dans l'autre sens.
.1b = .5d, .01b = .25d, .001b = .125, ...
Le problème est qu'il n'y a pas de moyen exact de représenter la plupart des nombres décimaux tels que .1, .2, .3, etc. Tout ce que vous pouvez faire est une approximation en binaire. Le système fait un peu de fudge-arrondi lorsque les nombres s'impriment pour afficher 0,1 au lieu de .10000000000001 ou .999999999999 (qui sont probablement aussi proches de la représentation stockée que .1)
Edit from comment: La raison pour laquelle c'est un problème est nos attentes. Nous nous attendons à ce que 2/3 soit truqué à un moment donné lorsque nous le convertissons en décimal, soit 0,7, 0,67 ou 0,666667 .. Mais nous ne nous attendons pas automatiquement à ce que 0,1 soit arrondi de la même manière que 2/3 - et c'est exactement ce qui se passe.
Au fait, si vous êtes curieux, le nombre qu'il stocke en interne est une pure représentation binaire utilisant une "notation scientifique" binaire. Donc, si vous lui dites de stocker le nombre décimal 10,75d, il stockera 1010b pour le 10 et .11b pour la décimale. Donc, il stockerait .101011 puis il économisait quelques bits à la fin pour dire: Déplacez le point décimal de quatre places vers la droite.
(Bien que techniquement ce ne soit plus un point décimal, c'est maintenant un point binaire, mais cette terminologie n'aurait pas rendu les choses plus compréhensibles pour la plupart des gens qui trouveraient cette réponse de n'importe quelle utilité.)
la source
Parce que ce n'est pas vrai que
0.1 + 0.2 == 0.3
la source
Float.compare(0.1f+0.2f, 0.3f) == 0
?Je pense qu'il y a beaucoup de confusion autour des flotteurs (et des doubles), il est bon de le clarifier.
Il n'y a rien de mal en soi à utiliser des flottants comme ID dans la JVM conforme à la norme [*]. Si vous définissez simplement l'ID flottant sur x, ne faites rien avec (c'est-à-dire pas d'arithmétique) et testez plus tard pour y == x, tout ira bien. De plus, il n'y a rien de mal à les utiliser comme clés dans un HashMap. Ce que vous ne pouvez pas faire, c'est supposer des égalités telles que
x == (x - y) + y
, etc. . Notez qu'il y a autant dedouble
valeurs différentes que de valeurs longuesvalues
, donc vous ne gagnez rien en utilisantdouble
. De plus, la génération du «prochain ID disponible» peut être délicate avec les doubles et nécessite une certaine connaissance de l'arithmétique à virgule flottante. Ne vaut pas la peine.En revanche, s'appuyer sur l'égalité numérique des résultats de deux calculs mathématiquement équivalents est risqué. Cela est dû aux erreurs d'arrondi et à la perte de précision lors de la conversion de la représentation décimale en représentation binaire. Cela a été discuté à mort sur SO.
[*] Quand j'ai dit «JVM conforme aux normes», je voulais exclure certaines implémentations de JVM endommagées par le cerveau. Regarde ça .
la source
==
plutôt que deequals
, soit de s'assurer qu'aucun flottant qui se compare inégal à lui-même n'est stocké dans une table. Sinon, un programme qui essaie, par exemple, de compter combien de résultats uniques peuvent être produits à partir d'une expression lorsqu'il est alimenté par diverses entrées, peut considérer chaque valeur NaN comme unique.Float
, pas àfloat
.Float
? Si l'on essaie de construire une table defloat
valeurs uniques et de les comparer avec==
, les horribles règles de comparaison IEEE-754 entraîneront l'inondation de la table avec desNaN
valeurs.float
le type n'a pas deequals
méthode.equals
méthode d'instance, mais plutôt de la méthode statique (je pense au sein de laFloat
classe) qui compare deux valeurs de typefloat
.Il s'agit d'un problème non spécifique à java. L'utilisation de == pour comparer deux flottants / doubles / tout nombre de type décimal peut potentiellement causer des problèmes en raison de la façon dont ils sont stockés. Un float simple précision (selon la norme IEEE 754) a 32 bits, répartis comme suit:
1 bit - Signe (0 = positif, 1 = négatif)
8 bits - Exposant (une représentation spéciale (biais-127) du x en 2 ^ x)
23 bits - Mantisa. Le numéro d'actuall enregistré.
La mantisa est ce qui cause le problème. C'est un peu comme la notation scientifique, seul le nombre en base 2 (binaire) ressemble à 1.110011 x 2 ^ 5 ou quelque chose de similaire. Mais en binaire, le premier 1 est toujours un 1 (sauf pour la représentation de 0)
Par conséquent, pour économiser un peu d'espace mémoire (jeu de mots), l'IEEE a décidé que le 1 devait être supposé. Par exemple, une mantisa de 1011 vaut vraiment 1.1011.
Cela peut causer des problèmes de comparaison, en particulier avec 0 car 0 ne peut pas être représenté exactement dans un flottant. C'est la principale raison pour laquelle le == est déconseillé, en plus des problèmes mathématiques en virgule flottante décrits par d'autres réponses.
Java a un problème unique en ce que le langage est universel sur de nombreuses plates-formes différentes, chacune d'entre elles pouvant avoir son propre format flottant unique. Cela rend encore plus important d'éviter ==.
La bonne façon de comparer deux flottants (non spécifique à la langue, pensez à l'égalité) pour l'égalité est la suivante:
où ACCEPTABLE_ERROR est #defined ou une autre constante égale à 0,00000000001 ou toute précision requise, comme Victor l'a déjà mentionné.
Certaines langues ont cette fonctionnalité ou cette constante intégrée, mais c'est généralement une bonne habitude à adopter.
la source
À partir d'aujourd'hui, le moyen rapide et facile de le faire est:
Cependant, la documentation ne spécifie pas clairement la valeur de la différence de marge (un epsilon de la réponse de @Victor) qui est toujours présente dans les calculs sur les flottants, mais cela devrait être quelque chose de raisonnable car il fait partie de la bibliothèque de langage standard.
Pourtant, si une précision supérieure ou personnalisée est nécessaire, alors
est une autre option de solution.
la source
(sectionId == currentSectionId)
ce qui n'est pas précis pour les virgules flottantes. la méthode epsilon est la meilleure approche, qui se trouve dans cette réponse: stackoverflow.com/a/1088271/4212710Les valeurs des points de Foating ne sont pas fiables, en raison d'une erreur d'arrondi.
En tant que tels, ils ne devraient probablement pas être utilisés comme valeurs de clé, telles que sectionID. Utilisez plutôt des entiers, ou
long
siint
ne contient pas suffisamment de valeurs possibles.la source
double
sont beaucoup plus précis, mais ce sont aussi des valeurs à virgule flottante, donc ma réponse était censée inclure à la foisfloat
etdouble
.En plus des réponses précédentes, vous devez être conscient qu'il existe des comportements étranges associés à
-0.0f
et+0.0f
(ils le sont==
mais pasequals
) etFloat.NaN
(c'estequals
mais pas==
) (j'espère que j'ai bien compris - argh, ne le faites pas!).Edit: Vérifions!
Bienvenue dans IEEE / 754.
la source
==
cela ne signifie pas que les nombres sont "identiques au bit" (le même nombre peut être représenté avec différents modèles de bits, bien qu'un seul d'entre eux soit de forme normalisée). De plus,-0.0f
et0.0f
sont représentés par différents modèles de bits (le bit de signe est différent), mais comparez comme égal à==
(mais pas avecequals
). Votre hypothèse de==
comparaison au niveau du bit est, d'une manière générale, erronée.Voici une discussion très longue (mais j'espère utile) à ce sujet et sur de nombreux autres problèmes de virgule flottante que vous pourriez rencontrer: Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante
la source
Vous pouvez utiliser Float.floatToIntBits ().
la source
Tout d'abord, sont-ils flottants ou flottants? Si l'un d'eux est un Float, vous devez utiliser la méthode equals (). De plus, il est probablement préférable d'utiliser la méthode statique Float.compare.
la source
Ce qui suit utilise automatiquement la meilleure précision:
Bien sûr, vous pouvez choisir plus ou moins de 5 ULP («unité à la dernière place»).
Si vous êtes dans la bibliothèque Apache Commons, la
Precision
classe acompareTo()
etequals()
avec à la fois epsilon et ULP.la source
double
couvrir cela.vous voudrez peut-être que ce soit ==, mais 123.4444444444443! = 123.4444444444442
la source
Si vous * devez * utiliser des flottants, le mot-clé strictfp peut être utile.
http://en.wikipedia.org/wiki/strictfp
la source
Deux calculs différents qui produisent des nombres réels égaux ne produisent pas nécessairement des nombres à virgule flottante égaux. Les gens qui utilisent == pour comparer les résultats des calculs finissent généralement par être surpris par cela, donc l'avertissement aide à signaler ce qui pourrait autrement être un bogue subtil et difficile à reproduire.
la source
Traitez-vous du code externalisé qui utiliserait des flottants pour les éléments nommés sectionID et currentSectionID? Juste curieux.
@Bill K: "La représentation binaire d'un float est assez ennuyeuse." Comment? Comment feriez-vous mieux? Il y a certains nombres qui ne peuvent être représentés correctement dans aucune base, car ils ne finissent jamais. Pi est un bon exemple. Vous ne pouvez que l'approcher. Si vous avez une meilleure solution, contactez Intel.
la source
Comme mentionné dans d'autres réponses, les doubles peuvent avoir de petits écarts. Et vous pouvez écrire votre propre méthode pour les comparer en utilisant un écart «acceptable». Cependant ...
Il existe une classe Apache pour comparer les doubles: org.apache.commons.math3.util.Precision
Il contient quelques constantes intéressantes:
SAFE_MIN
etEPSILON
, qui sont les écarts maximums possibles des opérations arithmétiques simples.Il fournit également les méthodes nécessaires pour comparer, égaler ou arrondir les doubles. (en utilisant des ulps ou une déviation absolue)
la source
En une seule ligne de réponse, je peux dire, vous devriez utiliser:
Pour vous en apprendre davantage sur l'utilisation correcte des opérateurs associés, j'élabore ici quelques cas: En général, il existe trois façons de tester des chaînes en Java. Vous pouvez utiliser ==, .equals () ou Objects.equals ().
Comment sont-ils différents? == teste la qualité de référence dans les chaînes, ce qui signifie savoir si les deux objets sont identiques. D'autre part, .equals () teste si les deux chaînes sont logiquement de valeur égale. Enfin, Objects.equals () teste les valeurs nulles dans les deux chaînes puis détermine s'il faut appeler .equals ().
Opérateur idéal à utiliser
Eh bien, cela a fait l'objet de nombreux débats car chacun des trois opérateurs a son ensemble unique de forces et de faiblesses. Exemple, == est souvent une option préférée lors de la comparaison de la référence d'objet, mais il y a des cas où il peut sembler comparer des valeurs de chaîne également.
Cependant, ce que vous obtenez est une valeur de baisse parce que Java crée l'illusion que vous comparez des valeurs mais que vous ne l'êtes pas vraiment. Considérez les deux cas ci-dessous:
Cas 1:
Cas 2:
Il est donc préférable d'utiliser chaque opérateur lors du test de l'attribut spécifique pour lequel il est conçu. Mais dans presque tous les cas, Objects.equals () est un opérateur plus universel et les développeurs Web optent donc pour cela.
Ici, vous pouvez obtenir plus de détails: http://fluentthemes.com/use-compare-strings-java/
la source
La bonne façon serait
la source
Float.compare(1.1 + 2.2, 3.3) != 0
Une façon de réduire l'erreur d'arrondi consiste à utiliser double plutôt que float. Cela ne résoudra pas le problème, mais cela réduira le nombre d'erreurs dans votre programme et float n'est presque jamais le meilleur choix. A MON HUMBLE AVIS.
la source