int
et
long
. Les deux types flottants sont
float
et
double
. Java ne différe pas des autres langages sur ce point.
Le principe est qu'une opération se déroule toujours en double ou simple précision pour les opérations flottantes, et en
long
ou
int
pour les opérations entières. Notamment, le résultat de l'addition de deux
short
en Java est un
int
.
Voici le détail des règles de conversion :
double
alors le calcul est effectué en
double
;
float
, alors le calcul est effectué en
float
;
long
alors le calcul est effectué en
long
;
int
.
float
et d'un
long
commence par la conversion de ce long en
float
. L'addition est ensuite effectuée, et le résultat est un
float
.
x
peut toujours s'écrire de la manière suivante, où
a
est compris entre 1 et 10 :
x = ± a.10^nDans cette écriture
a
est appelé
mantisse
et
n
exposant
. Chacun de ces nombres est codé sur un certain nombre de bits, qui dépend du type
float
ou
double
. La façon de coder ces nombres est précisée par la norme IEEE 754, que suit Java. Le premier type correspond au « simple précision » et est codé sur 32 bits, le second, « double précision » est codé sur 64 bits. L’allocation des bits suit le tableau suivant.
Tableau 5. Codage des nombres en virgule flottante
Signe | Exposant | Mantisse | |
---|---|---|---|
float
|
1 | 8 bits, signés | 23 bits |
double
|
1 | 11 bits, signés | 52 bits |
Exemple 59. Imprécision des flottants - 1
float f = (float)0.0 ; // 0.0 est un double, on le caste donc en float, // on aurait pu écrire aussi 0.0f ou 0.0F for (int i = 0 ; i < 10 ; i++) { f += (float) 0.1 ; } System.out.println("Valeur de f = " + f) ;
> Valeur de f = 1.0000001On peut refaire le test, en remplaçant le type
float
par
double
. Le résultat est effectivement un peu plus précis.
> Valeur de f = 0.9999999999999999Voici un dernier exemple, cette fois-ci sur la conversion d'un entier en flottant.
Exemple 60. Imprécision sur les flottants - 2
long l = 1000000000000000000L ; // 1e18 : 1 milliard de milliards ! float f = (float) l ; l = (long) f ; System.out.println("Valeur de l : " + l) ;
> Valeur de l : 999999984306749440Contrairement à l'exemple précédent, le passage de cette conversion vers la double précision conserve la valeur de l'entier, puisqu'il y a suffisament de bits pour coder cette valeur sans erreur. On voit sur ces deux exemples que la manipulation des nombres en virgules flottantes est parfois délicate. En particulier, tenter un test d'égalité entre deux nombres flottants mène le plus souvent à des problèmes. On préfèrera systématiquement tester la valeur absolue normalisée de la différence, comme dans l'exemple suivant.
Exemple 61. Tester l'égalité de deux flottants
float f1 = grosCalcul(...) ; // une valeur float f2 = autreGrosCalul(...) ; // une autre valeur if (f1 == f2) { victoire() ; // ce code a toutes les chances de ne jamais être executé ! } if (Math.abs(f1 - f2)/Math.max(f1, f2) < 1e-5) { // 1e-5 règle la précision // de l'"égalité" victoire() ; }
stricfp
. Ce mot-clé s'utilise en tête de déclaration d'une méthode, et impose que tous les calculs effectués dans cette méthode seront menés en simple précision.
Effectivement, la plupart des processeurs modernes dispose d'une FPU (floating point unit), chargée d'exécuter rapidement les calculs en virgule flottante. Ces FPU sont conçues pour opérer sur des
double
, et sont systématiquement appelées lorsque le processeur a besoin de faire une opération flottante, même quand il doit la faire sur des
float
. Un calcul déclaré en flottant est donc effectué en double précision, puis son résultat est converti en
float
.
Or, tous les processeurs ne sont pas équipés de cette FPU. Deux calculs écrits de la même façon peuvent donc mener à des résultats légèrement différents suivant le processeur qui les prend en charge, et notamment suivant que ce calcul s'effectue sur une FPU avec conversion ou non. Ce point viole le principe de portabilité de Java. Le mot-clé
stricfp
a donc été introduit, et permet de parer ce problème en fixant le mode de calcul, au détriment éventuel de la performance.
Exemple 62. Utilisation de
strictfp
strictfp float monCalcul(...) { // syntaxe d'utilisation de strifp
... ;
}
cast
explicite pour qu'elle se fasse.
Exemple 63. Conversion d'un
long
en
int
long j = 0 ; int i = 0 ; j = i ; // conversion par défaut d'un int en long i = j ; // ERREUR DE COMPILATION !!!!! i = (int)j ; // syntaxe correcte
int
soit en
long
, même si les deux opérandes sont de même type. Si l'on fait des opérations sur des
byte
par exemple, des conversions de type peuvent être nécessaires.
Exemple 64. Addition d'un
int
à un
byte
byte b = 0 ; b += 1 ; // code correct b = b + 1 ; // NE COMPILE PAS !!! car b + 1 est de type int b = b + (byte)1 ; // NE COMPILE PAS PLUS !!! car b + 1 est toujours un int b = (byte)(b + 1) ; // code correct
+
a généré la conversion en
int
des deux opérandes. On s’en rend compte puisque le fait de forcer l’utilisation d’un byte pour le 1 ne change rien. Le résultat est donc un
int
, que l’on veut affecter à un
byte
. L’utilisation d’une conversion explicite permet de lever l’alerte.
String
StringBuffer
et
StringBuilder