web-dev-qa-db-fra.com

Perte de précision - int -> float ou double

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?

23
stan

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

48
Dead account

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.
  • Certains floats représentent des non-entiers, donc il y a moins que 2 ^ 32 float valeurs qui représentent des entiers.
  • Par conséquent, différentes valeurs int seront converties en la même float (= perte de précision).

Un raisonnement similaire peut être utilisé avec long et double.

22
dan04

Voici ce que JLS a à dire sur le sujet (dans une discussion non technique).

JLS 5.1.2 Élargissement de la conversion des primitives

Les 19 conversions spécifiques suivantes sur les types primitifs sont appelées conversions de primitives avec élargissement:

  • int à long, float ou double
  • (reste omis)

La conversion d'une valeur int ou long en float, ou d'une valeur long en double, 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 type float car les valeurs de type float ne sont pas précises à neuf chiffres significatifs.

18

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.

13

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). 

4
Sinkrad

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.

4
Ian MacMillan

Il y a deux raisons pour lesquelles l'affectation d'un int à un double ou à un float peut perdre de la précision:

  • Certains nombres ne peuvent tout simplement pas être représentés par un double/float, ils finissent donc par se rapprocher
  • Les grands nombres entiers peuvent contenir trop de précision dans les chiffres significatifs du contrat de location
1
Adam Batkin

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.

0
HesNotTheStig