Pourquoi comparer des flotteurs en Java?

class Test{ public static void main(Ssortingng[] args){ float f1=3.2f; float f2=6.5f; if(f1==3.2){ System.out.println("same"); }else{ System.out.println("different"); } if(f2==6.5){ System.out.println("same"); }else{ System.out.println("different"); } } } 

sortie:

 different same 

Pourquoi la sortie est comme ça? Je m’attendais au same résultat que le premier cas.

La différence est que 6.5 peut être représenté exactement dans float et double, alors que 3.2 ne peut pas être représenté exactement dans les deux types. et les deux approximations les plus proches sont différentes.

Une comparaison d’égalité entre float et double convertit d’abord le flottant en double et compare ensuite les deux. Donc, la perte de données.


Vous ne devriez jamais comparer des flotteurs ou des doubles pour l’égalité; car vous ne pouvez pas vraiment garantir que le numéro que vous atsortingbuez au float ou au double est exact.

Cette erreur d’arrondi est une caractéristique du calcul en virgule flottante .

Presser une infinité de nombres réels dans un nombre fini de bits nécessite une représentation approximative. Bien qu’il y ait une infinité de nombres entiers, dans la plupart des programmes, le résultat des calculs en nombre entier peut être stocké en 32 bits.

En revanche, avec un nombre de bits fixe, la plupart des calculs avec des nombres réels produiront des quantités qui ne peuvent pas être exactement représentées en utilisant autant de bits. Par conséquent, le résultat d’un calcul en virgule flottante doit souvent être arrondi pour revenir dans sa représentation finie. Cette erreur d’arrondi est la caractéristique du calcul en virgule flottante.

Vérifiez ce que chaque informaticien devrait savoir sur l’arithmétique en virgule flottante pour en savoir plus!

Ce sont deux implémentations de différentes parties de la norme IEEE en virgule flottante . Un float est large de 4 octets, tandis qu’un double est large de 8 octets.

En règle générale, vous devriez probablement utiliser le double dans la plupart des cas, et utiliser uniquement le float lorsque vous avez une bonne raison de le faire. (Un exemple de bonne raison d’utiliser float par opposition à un double est “je sais que je n’ai pas besoin de beaucoup de précision et j’en ai besoin d’enregistrer un million en mémoire”). vous n’avez pas besoin de double précision.

En outre, lorsque vous comparez des valeurs à virgule flottante pour l’égalité, vous souhaiterez généralement utiliser quelque chose comme Math.abs(ab) < EPSILONa et b sont les valeurs à virgule flottante comparées et EPSILON est une petite valeur à virgule flottante comme 1e-5 . La raison en est que les valeurs à virgule flottante codent rarement la valeur exacte qu'elles "devraient" - elles encodent généralement une valeur très proche - il faut donc "squinter" lorsque vous déterminez si deux valeurs sont identiques.

EDIT : Tout le monde devrait lire le lien @Kugathasan Abimaran posté ci-dessous: Ce que chaque informaticien devrait savoir sur l'arithmétique en virgule flottante pour plus!

Pour voir ce que vous traitez, vous pouvez utiliser la méthode toHexSsortingng de Float et Double :

 class Test { public static void main(Ssortingng[] args) { System.out.println("3.2F is: "+Float.toHexSsortingng(3.2F)); System.out.println("3.2 is: "+Double.toHexSsortingng(3.2)); System.out.println("6.5F is: "+Float.toHexSsortingng(6.5F)); System.out.println("6.5 is: "+Double.toHexSsortingng(6.5)); } } $ java Test 3.2F is: 0x1.99999ap1 3.2 is: 0x1.999999999999ap1 6.5F is: 0x1.ap2 6.5 is: 0x1.ap2 

Généralement, un nombre a une représentation exacte s’il est égal à A * 2 ^ B, où A et B sont des entiers dont les valeurs autorisées sont définies par la spécification de langue (et que le double a plus de valeurs autorisées).

Dans ce cas,
6,5 = 13/2 = (1 + 10/16) * 4 = (1 + a / 16) * 2 ^ 2 == 0x1.ap2, tandis que
3.2 = 16/5 = (1 + 9/16 + 9/16 ^ 2 + 9/16 ^ 3 +…) * 2 ^ 1 == 0x1.999. . . p1.
Mais Java ne peut contenir qu’un nombre fini de chiffres, il coupe donc le .999. . . éteint à un moment donné. (Vous vous souvenez peut-être des maths que 0,999.. = 1. C’est dans la base 10. Dans la base 16, ce serait 0.fff.. = 1.)

 class Test { public static void main(Ssortingng[] args) { float f1=3.2f; float f2=6.5f; if(f1==3.2f) System.out.println("same"); else System.out.println("different"); if(f2==6.5f) System.out.println("same"); else System.out.println("different"); } } 

Essayez comme ça et ça va marcher. Sans ‘f’, vous comparez un flottant avec un autre type flottant et une précision différente qui peut entraîner un résultat inattendu comme dans votre cas.

Il n’est pas possible de comparer les valeurs de type float et double directement. Avant de pouvoir comparer les valeurs, il est nécessaire soit de convertir le double pour float , soit de le convertir en double . Si on fait la comparaison précédente, la conversion demandera “Est-ce que le float contient la meilleure représentation possible de la valeur du double ?” Si on fait la dernière conversion, la question sera “Le float contient-il une représentation parfaite de la valeur du double “. Dans de nombreux contextes, la première question est la plus significative, mais Java suppose que toutes les comparaisons entre float et double sont destinées à poser la dernière question.

Je suggère que peu importe ce que la langue est prête à tolérer, ses normes de codage devraient absolument interdire les comparaisons directes entre les opérandes de type float et double . Code donné comme:

 float f = function1(); double d = function2(); ... if (d==f) ... 

il est impossible de dire quel comportement est prévu dans les cas où d représente une valeur qui n’est pas précisément représentable dans float . Si l’intention est que f soit converti en double et que le résultat de cette conversion soit comparé à d , il faut écrire la comparaison en tant que

 if (d==(double)f) ... 

Bien que le transtypage ne modifie pas le comportement du code, il est clair que le comportement du code est intentionnel. Si l’intention était que la comparaison indique si f détient la meilleure représentation float de d , elle devrait être:

 if ((float)d==f) 

Notez que le comportement de ceci est très différent de ce qui se passerait sans la dissortingbution. Si votre code d’origine avait lancé l’opérande double de chaque comparaison pour float , les deux tests d’égalité seraient passés.

En général, il n’est pas recommandé d’utiliser l’opérateur == avec un nombre à virgule flottante, en raison de problèmes d’approximation.

6.5 peut être représenté exactement en binary, alors que 3.2 ne le peut pas. C’est pourquoi la différence de précision n’a pas d’importance pour 6.5, donc 6.5 == 6.5f .

Pour actualiser rapidement le fonctionnement des nombres binarys:

 100 -> 4 10 -> 2 1 -> 1 0.1 -> 0.5 (or 1/2) 0.01 -> 0.25 (or 1/4) 

etc.

6.5 en binary: 110.1 (résultat exact, le rest des chiffres ne sont que des zéros)

3.2 en binary: 11.001100110011001100110011001100110011001100110011001101... (ici la précision compte!)

Un float n’a qu’une précision de 24 bits (le rest est utilisé pour les signes et les exposants), donc:

3.2f en binary: 11.0011001100110011001100 (pas égal à l’approximation en double précision)

En gros, c’est la même chose que lorsque vous écrivez 1/5 et 1/7 en nombres décimaux:

 1/5 = 0,2 1,7 = 0,14285714285714285714285714285714. 

Float a moins de précision que le double, bcoz float utilise 32bits dans lesquels 1 est utilisé pour Sign, 23 précision et 8 pour Exponent. Là où le double utilise 64 bits dans lesquels 52 sont utilisés pour la précision, 11 pour l’exposant et 1 pour le signe …. La précision est importante. Un nombre décimal représenté par flottant et double peut être égal ou différent dépend du besoin de précision des nombres après le point décimal peuvent varier). Cordialement S. ZAKIR