Je suis en train de réviser une question d’examen qui porte sur 4 points.
"En Java, on peut assigner un int à un double ou à un float". Cela perdra-t-il des informations et pourquoi?
J'ai dit cela parce que les ints ont normalement une longueur ou une taille fixe - la précision du stockage des données est finie, le stockage des informations en virgule flottante pouvant être infini, nous perdons essentiellement des informations à cause de cela
Maintenant, je suis un peu rusé quant à savoir si je frappe ou non aux bons endroits ici. Je suis persuadé que cela perdra de la précision, mais je ne sais pas pourquoi. Puis-je obtenir de l'aide, s'il vous plaît?
En Java, Integer utilise 32 bits pour représenter sa valeur.
En Java, FLOAT utilise une mantisse de 23 bits, ainsi les entiers supérieurs à 2 ^ 23 verront leurs bits les moins significatifs tronqués. Par exemple, 33554435 (ou 0x200003) sera tronqué à environ 33554432 +/- 4
En Java, un DOUBLE utilise une mantisse de 52 bits, ce qui lui permet de représenter un entier 32 bits sans perte de données.
Voir aussi " Floating Point " sur wikipedia
Il n'est pas nécessaire de connaître la disposition interne des nombres à virgule flottante. Tout ce dont vous avez besoin, c'est du principe du casier et de la connaissance que int
et float
ont la même taille.
int
est un type 32 bits, pour lequel chaque motif de bits représente un entier distinct. Il existe donc 2 ^ 32 int
valeurs.float
est un type 32 bits, il a donc au plus 2 ^ 32 valeurs distinctes.float
s représentent des non-entiers, donc il y a moins que 2 ^ 32 float
valeurs qui représentent des entiers.int
seront converties en la même float
(= perte de précision).Un raisonnement similaire peut être utilisé avec long
et double
.
Voici ce que JLS a à dire sur le sujet (dans une discussion non technique).
Les 19 conversions spécifiques suivantes sur les types primitifs sont appelées conversions de primitives avec élargissement:
int
àlong
,float
oudouble
- (reste omis)
La conversion d'une valeur
int
oulong
enfloat
, ou d'une valeurlong
endouble
, peut entraîner perte de précision -, c'est-à-dire que le résultat peut perdre certains des bits les moins significatifs de la valeur. Dans ce cas, la valeur en virgule flottante résultante sera une version correctement arrondie de la valeur entière, en utilisant le mode arrondi au plus proche IEEE 754.Malgré le risque de perte de précision, l'élargissement des conversions entre types primitifs n'aboutit jamais à une exception d'exécution.
Voici un exemple de conversion d'élargissement qui perd de la précision:
class Test { public static void main(String[] args) { int big = 1234567890; float approx = big; System.out.println(big - (int)approx); } }
qui imprime:
-46
cela indique donc que des informations ont été perdues lors de la conversion de type
int
en typefloat
car les valeurs de typefloat
ne sont pas précises à neuf chiffres significatifs.
Non, float
et double
sont aussi de longueur fixe - ils utilisent simplement leurs bits différemment. En savoir plus sur la manière dont ils travaillent exactement dans le Guide de navigation flottante .
En gros, vous ne pouvez pas perdre en précision lorsque vous assignez une int
à une double
, car double
a 52 bits de précision, ce qui suffit pour contenir toutes les valeurs int
. Mais float
a seulement 23 bits de précision, il ne peut donc pas représenter exactement toutes les valeurs int
qui sont supérieures à environ 2 ^ 23.
Votre intuition est correcte, vous POUVEZ perdre de la précision lors de la conversion de int
en float
. Cependant, ce n'est pas aussi simple que présenté dans la plupart des autres réponses.
En Java, FLOAT utilise une mantisse de 23 bits, ainsi les entiers supérieurs à 2 ^ 23 verront leurs bits les moins significatifs tronqués. (extrait d'un post sur cette page)
Ce n'est pas vrai.
Exemple: voici un entier supérieur à 2 ^ 23 qui se transforme en float sans perte:
int i = 33_554_430 * 64; // is greater than 2^23 (and also greater than 2^24); i = 2_147_483_520
float f = i;
System.out.println("result: " + (i - (int) f)); // Prints: result: 0
System.out.println("with i:" + i + ", f:" + f);//Prints: with i:2_147_483_520, f:2.14748352E9
Par conséquent, il n’est pas vrai que les entiers supérieurs à 2 ^ 23 verront leurs bits les moins significatifs tronqués.
_ {La meilleure explication que j'ai trouvée est ici:
Un float en Java est 32 bits et est représenté par:
signe * mantisse * 2 ^ exposant
signe * (0 à 33_554_431) * 2 ^ (- 125 à +127)
Source: http://www.ibm.com/developerworks/Java/library/j-math2/index.html
Pourquoi est-ce un problème?
Cela donne l’impression que vous pouvez déterminer s’il existe une perte de précision entre int et float en regardant simplement la taille int est.
J'ai surtout vu des questions sur l'examen Java où l'on se demandait si un grand int se convertirait en float sans perte.
De plus, parfois, les gens ont tendance à penser qu’il y aura une perte de précision d’int à float:
quand un int est plus grand que: 1_234_567_890 pas vrai (voir le contre-exemple ci-dessus)}
quand un int est plus grand que: 2 exposant 23 (est égal à: 8_388_608) pas vrai
quand un int est plus grand que: 2 exposant 24 (est égal à: 16_777_216) pas vrai
Conclusion
Les conversions d’ints suffisamment grands en flotteurs PEUVENT perdre de la précision.
Il n’est pas possible de déterminer s’il y aura une perte simplement en regardant à la taille de l’int (c’est-à-dire sans essayer d’approfondir la représentation flottante réelle).
Peut-être l'explication la plus claire que j'ai vue: http://www.ibm.com/developerworks/Java/library/j-math2/index.html L'ULP ou l'unité de moindre précision définit la précision disponible entre deux valeurs quelconques. Au fur et à mesure que ces valeurs augmentent, la précision disponible diminue. Par exemple: entre 1.0 et 2.0 inclus, il y a 8.388.609 flottants, entre 1.000.000 et 1.000.001, il y en a 17. À 10.000.000, le ULP est 1.0, donc au-dessus de cette valeur les valeurs entières mappant sur chaque float disponible, d’où la perte de précision.
Il y a deux raisons pour lesquelles l'affectation d'un int à un double ou à un float peut perdre de la précision:
Pour ces exemples, j'utilise Java.
Utilisez une fonction comme celle-ci pour vérifier la perte de précision lors de la conversion de int à float
static boolean checkPrecisionLossToFloat(int val)
{
if(val < 0)
{
val = -val;
}
// 8 is the bit-width of the exponent for single-precision
return Integer.numberOfLeadingZeros(val) + Integer.numberOfTrailingZeros(val) < 8;
}
Utilisez une fonction comme celle-ci pour vérifier la perte de précision lors de la conversion de long en double.
static boolean checkPrecisionLossToDouble(long val)
{
if(val < 0)
{
val = -val;
}
// 11 is the bit-width for the exponent in double-precision
return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 11;
}
Utilisez une fonction comme celle-ci pour vérifier la perte de précision lors de la conversion de long en flottant
static boolean checkPrecisionLossToFloat(long val)
{
if(val < 0)
{
val = -val;
}
// 8 + 32
return Long.numberOfLeadingZeros(val) + Long.numberOfTrailingZeros(val) < 40;
}
Pour chacune de ces fonctions, renvoyer true signifie que la conversion de cette valeur intégrale en valeur à virgule flottante entraînera une perte de précision.
La conversion en valeur flottante perdra en précision si la valeur intégrale a plus de 24 bits significatifs.
La conversion en double perdra de la précision si la valeur intégrale a plus de 53 bits significatifs.