class Test{
public static void main(String[] 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-elle comme ça? Je m'attendais à same
comme résultat dans le premier cas.
La différence est que 6.5 peut être représenté exactement à la fois en float et en double, tandis que 3.2 ne peut être représenté exactement dans aucun des deux types. et les deux approximations les plus proches sont différentes.
Une comparaison d'égalité entre float et double convertit d'abord le float en double, puis compare les deux. Ainsi, la perte de données.
Vous ne devriez jamais comparer des flottants ou des doubles pour l'égalité; car vous ne pouvez pas vraiment garantir que le numéro que vous attribuez au flottant ou au double est exact.
Cette erreur d'arrondi est une caractéristique du calcul à virgule flottante.
La compression d'une infinité de nombres réels en un nombre fini de bits nécessite une représentation approximative. Bien qu'il existe une infinité de nombres entiers, dans la plupart des programmes, le résultat des calculs de nombres entiers peut être stocké sur 32 bits.
En revanche, étant donné un nombre fixe de bits, la plupart des calculs avec des nombres réels produisent des quantités qui ne peuvent pas être représentées exactement en utilisant autant de bits. Par conséquent, le résultat d'un calcul en virgule flottante doit souvent être arrondi pour pouvoir revenir dans sa représentation finie. Cette erreur d'arrondi est la caractéristique du calcul en virgule flottante.
Vérifiez ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante pour en savoir plus!
Ce sont deux implémentations de différentes parties de la norme à virgule flottante IEEE . Un float
a une largeur de 4 octets, tandis qu'un double
a une largeur de 8 octets.
En règle générale, vous devriez probablement préférer utiliser double
dans la plupart des cas, et n'utiliser float
que si vous avez une bonne raison de le faire. (Un exemple d'une bonne raison d'utiliser float
par opposition à double
est "I know Je n'ai pas besoin de beaucoup de précision et J'ai besoin d'en stocker un million en mémoire. ") Il convient également de mentionner qu'il est difficile de prouver que vous n'avez pas besoin de double
précision.
De plus, lorsque vous comparez des valeurs à virgule flottante pour l'égalité, vous souhaiterez généralement utiliser quelque chose comme Math.abs(a-b) < EPSILON
où a
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" - plutôt, elles codent généralement une valeur très proche - vous devez donc "plisser les yeux" lorsque vous déterminez si deux valeurs sont identiques.
[~ # ~] modifier [~ # ~] : Tout le monde devrait lire le lien @Kugathasan Abimaran affiché ci-dessous: Ce que tout informaticien devrait En savoir plus sur l'arithmétique à virgule flottante!
Pour voir ce que vous traitez, vous pouvez utiliser la méthode toHexString de Float et Double :
class Test {
public static void main(String[] args) {
System.out.println("3.2F is: "+Float.toHexString(3.2F));
System.out.println("3.2 is: "+Double.toHexString(3.2));
System.out.println("6.5F is: "+Float.toHexString(6.5F));
System.out.println("6.5 is: "+Double.toHexString(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 du langage (et 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, donc il coupe le .999 ... à un moment donné. = 1. C'est en base 10. En base 16, ce serait 0.fff... = 1.)
class Test {
public static void main(String[] 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 marchera. Sans "f", vous comparez un flottant avec un autre type flottant et une précision différente, ce qui peut entraîner un résultat inattendu comme dans votre cas.
Il n'est pas possible de comparer directement les valeurs de type float
et double
. Avant de pouvoir comparer les valeurs, il est nécessaire de convertir le double
en float
, ou de convertir le float
en double
. Si l'on fait la comparaison précédente, la conversion demandera "La float
contient-elle la meilleure représentation float
possible de la valeur de double
?" Si l'on fait cette dernière conversion, la question sera "Est-ce que le float
contient 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
visent à poser la dernière question.
Je suggérerais qu'indépendamment de ce qu'un langage est prêt à tolérer, ses normes de codage devraient absolument interdire positivement les comparaisons directes entre les opérandes de type float
et double
. Étant donné le code 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 sous la forme
if (d==(double)f) ...
Bien que le transtypage ne modifie pas le comportement du code, il indique clairement que le comportement du code est intentionnel. Si l'intention était que la comparaison indique si f
contient 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 le cast. Si votre code d'origine avait converti l'opérande double
de chaque comparaison en float
, les deux tests d'égalité auraient réussi.
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 binaire, contrairement à 3.2. 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 binaires:
100 -> 4
10 -> 2
1 -> 1
0.1 -> 0.5 (or 1/2)
0.01 -> 0.25 (or 1/4)
etc.
6.5 en binaire: 110.1
(résultat exact, les autres chiffres ne sont que des zéros)
3.2 en binaire: 11.001100110011001100110011001100110011001100110011001101...
(ici la précision compte!)
Un flottant n'a qu'une précision de 24 bits (le reste est utilisé pour le signe et l'exposant), donc:
3.2f en binaire: 11.0011001100110011001100
(différent de l'approximation double précision)
Fondamentalement, 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.