web-dev-qa-db-fra.com

Pourquoi Java implicitement (sans transtypage) convertit un `long` en un` float`?

Chaque fois que je pense comprendre le casting et les conversions, je trouve un autre comportement étrange.

long l = 123456789L;
float f = l;
System.out.println(f);  // outputs 1.23456792E8

Étant donné qu'un long a une plus grande profondeur de bits qu'un float, je m'attendrais à ce qu'une conversion explicite soit requise pour que cela se compile. Et sans surprise, nous constatons que nous avons perdu de la précision dans le résultat.

Pourquoi un casting n'est-il pas nécessaire ici?

36
Eric Wilson

La même question pourrait être posée de long à double - les deux conversions peuvent perdre des informations.

La section 5.1.2 de la Java dit:

L'élargissement des conversions primitives ne perd pas d'informations sur l'ampleur globale d'une valeur numérique. En effet, les conversions s'élargissant d'un type intégral à un autre type intégral ne perdent aucune information; la valeur numérique est conservée exactement. Les conversions qui s'élargissent de float à double dans les expressions strictfp préservent également la valeur numérique exactement; cependant, de telles conversions qui ne sont pas strictes peuvent perdre des informations sur l'amplitude globale de la valeur convertie.

La conversion d'une valeur int ou longue en float, ou d'une valeur longue en double, peut entraîner une 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 (§4.2.4).

En d'autres termes, même si vous risquez de perdre des informations, vous savez que la valeur sera toujours dans la plage globale du type cible.

Le choix aurait certainement pu être fait pour exiger que toutes les conversions implicites ne perdent aucune information - donc int et long à float auraient été explicites et long à double aurait été explicite. (int à double est correct; un double a suffisamment de précision pour représenter avec précision toutes les valeurs int.)

Dans certains cas, cela aurait été utile - dans certains cas, non. La conception du langage est un compromis; vous ne pouvez pas tous les gagner. Je ne sais pas quelle décision j'aurais prise ...

43
Jon Skeet

Java Language Specification, Chapter 5: Conversion and Promotion résout ce problème:

5.1.2 Élargir la conversion primitive

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

  • octet à court, entier, long, flottant ou double
  • court à int, long, flottant ou double
  • char à int, long, float ou double
  • int à long, flottant ou double
  • long pour flotter ou doubler
  • flotter pour doubler

L'élargissement des conversions primitives ne perd pas d'informations sur l'ampleur globale d'une valeur numérique.

...

La conversion d'une valeur int ou longue en float, ou d'une valeur longue en double, peut entraîner une 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 à virgule flottante résultante sera une version correctement arrondie de la valeur entière

En d'autres termes, le JLS fait la distinction entre une perte de magnitude et une perte de précision.

int à byte par exemple est une perte (potentielle) d'amplitude car vous ne pouvez pas stocker 500 dans un byte.

long à float est une perte potentielle de précision mais pas d'amplitude car la plage de valeurs pour les flottants est plus grande que celle pour les longs.

La règle est donc:

  • Perte d'amplitude: transtypage explicite requis;
  • Perte de précision: aucun lancer requis.

Subtil? Sûr. Mais j'espère que cela clarifie cela.

41
cletus

Bien que vous ayez raison de dire qu'un long utilise plus de bits en interne qu'un float, le langage Java fonctionne sur un chemin d'élargissement:

octet -> court -> int -> long -> float -> double

Pour convertir de gauche à droite (une conversion élargie), il n'y a pas de transtypage nécessaire (c'est pourquoi le temps de flotter est autorisé). Pour convertir de droite à gauche (une conversion plus étroite), une conversion explicite est nécessaire.

15
Peter

Quelque part, j'ai entendu ça. Float peut stocker sous forme exponentielle telle que nous l'écrivons. '23500000000' est stocké sous la forme '2.35e10'. Ainsi, float a de l'espace pour occuper la plage de valeurs de long. Le stockage sous forme exponentielle est également la raison de la perte de précision.

2
george