118 votes

Convertir un float en double sans perdre la précision

J'ai un float primitif et j'ai besoin d'un double primitif. En transformant simplement le flottant en double, j'obtiens une précision supplémentaire bizarre. Par exemple :

float temp = 14009.35F;
System.out.println(Float.toString(temp)); // Prints 14009.35
System.out.println(Double.toString((double)temp)); // Prints 14009.349609375

Cependant, si au lieu de faire un casting, j'affiche le flottant sous forme de chaîne de caractères, et que j'analyse la chaîne de caractères comme un double, j'obtiens ce que je veux :

System.out.println(Double.toString(Double.parseDouble(Float.toString(temp))));
// Prints 14009.35

Y a-t-il un meilleur moyen que d'aller à String et d'en revenir ?

140voto

Jon Skeet Points 692016

Ce n'est pas que vous êtes en fait obtenir une précision supplémentaire - c'est que le flottant ne représentait pas exactement le nombre que vous visiez à l'origine. Le double est représentant fidèlement le flotteur original ; toString montre les données "supplémentaires" qui étaient déjà présentes.

Par exemple (et ces chiffres ne sont pas exacts, je ne fais qu'inventer des choses) supposons que vous ayez :

float f = 0.1F;
double d = f;

Alors la valeur de f pourrait être exactement 0,100000234523. d aura exactement la même valeur, mais lorsque vous la convertirez en une chaîne de caractères, elle "croira" qu'elle est exacte avec une plus grande précision, et n'arrondira pas aussi tôt, et vous verrez les "chiffres supplémentaires" qui étaient déjà là, mais cachés.

Lorsque vous convertissez en chaîne de caractères et inversement, vous obtenez une valeur double qui est plus proche de la valeur de la chaîne de caractères que ne l'était le flottant d'origine - mais ce n'est qu'un avantage. si vous croyez vraiment que la valeur de la chaîne est ce que vous vouliez vraiment.

Etes-vous sûr que float/double sont les types appropriés à utiliser ici au lieu de BigDecimal ? Si vous essayez d'utiliser des nombres qui ont des valeurs décimales précises (par exemple, de l'argent), alors BigDecimal est un type plus approprié IMO.

49voto

Brecht Yperman Points 176

Je trouve que la conversion à la représentation binaire est plus facile pour appréhender ce problème.

float f = 0.27f;
double d2 = (double) f;
double d3 = 0.27d;

System.out.println(Integer.toBinaryString(Float.floatToRawIntBits(f)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d2)));
System.out.println(Long.toBinaryString(Double.doubleToRawLongBits(d3)));

Vous pouvez voir que le flottant est étendu au double en ajoutant des 0 à la fin, mais que la représentation double de 0,27 est "plus précise", d'où le problème.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000

27voto

erickson Points 127945

Ceci est dû au contrat de Float.toString(float) , qui dit en partie :

Combien de chiffres doivent être imprimés pour la partie fractionnaire [ ] ? Il y a doit y avoir au moins un chiffre pour représenter la partie fractionnaire, et au-delà, autant de chiffres que nécessaire, mais seulement autant, plus de chiffres qu'il n'en faut pour être unique. distinguer la valeur de l'argument des valeurs adjacentes de type float. Ce C'est-à-dire, supposons que x est la valeur mathématique exacte valeur mathématique représentée par la représentation décimale produite par cette méthode pour un argument f fini et non nul f. Alors f doit être la valeur flottante la plus proche de x ; ou, si deux valeurs flottantes sont également proches de x, alors f doit être l'une d'entre elles et le bit le moins significatif significatif du significande de f doit être égal à 0.

17voto

Andrew Points 51

J'ai rencontré ce problème aujourd'hui et je n'ai pas pu utiliser le refactor en BigDecimal, car le projet est vraiment énorme. Cependant, j'ai trouvé une solution en utilisant

Float result = new Float(5623.23)
Double doubleResult = new FloatingDecimal(result.floatValue()).doubleValue()

Et ça marche.

Notez que l'appel de result.doubleValue() renvoie 5623.22998046875

Mais l'appel à doubleResult.doubleValue() renvoie correctement 5623.23

Mais je ne suis pas tout à fait sûr qu'il s'agisse d'une solution correcte.

1 votes

Ça a marché pour moi. Il est également très rapide. Je ne sais pas pourquoi cette question n'est pas marquée comme une réponse.

0 votes

C'est la méthode que j'utilise, et vous n'avez pas besoin de la nouvelle partie Float... Créez simplement le FloatingDecimal avec la primitive. Il sera automatiquement encadré... Et ça marche vite aussi... Il y a un seul inconvénient, FloatingDecimal est immuable, donc vous devez en créer un pour chaque flotteur... imaginez un code de calcul de O(1e10) !!! Bien sûr, BigDecimal a le même inconvénient...

9 votes

L'inconvénient de cette solution est que sun.misc.FloatingDecimal est une classe interne de la JVM et que la signature de son constructeur a été modifiée dans Java 1.8. Personne ne devrait utiliser les classes internes dans une application réelle.

7voto

Aaron Digulla Points 143830

Utilisez un BigDecimal au lieu de float / double . Il y a beaucoup de nombres qui ne peuvent pas être représentés en virgule flottante binaire (par exemple, 0.1 ). Vous devez donc soit toujours arrondir le résultat à une précision connue, soit utiliser la méthode suivante BigDecimal .

Voir http://en.wikipedia.org/wiki/Floating_point pour plus d'informations.

0 votes

Disons que vous avez 10 millions de prix d'échanges de flotteurs dans votre cache, envisagez-vous toujours d'utiliser BigDecimal et d'instancier des objets uniques, disons ~6 millions (pour réduire le poids de l'avion/immuable) ou y a-t-il autre chose à faire ?

1 votes

@Sendi_t Veuillez ne pas utiliser les commentaires pour poser des questions complexes :-)

0 votes

Merci d'avoir regardé ! nous utilisons actuellement des flotteurs car cela a été convenu il y a une décennie -- j'essayais de voir votre point de vue sur cette situation sur le suivi -- merci !

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X