web-dev-qa-db-fra.com

Convertissez float en double sans perdre de précision

J'ai un float primitif et j'ai besoin d'un double primitif. Le simple fait de doubler le flotteur me donne une précision supplémentaire étrange. 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 lancer, je produis le float sous la forme d'une chaîne et que j'analyse la chaîne sous la forme d'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 retour?

83
Steve Armstrong

Ce n’est pas que vous en fait obteniez une précision supplémentaire - c’est que le flottant ne représentait pas le nombre que vous visiez à l’origine. Le double est / représente le float original avec précision; toString affiche les données "supplémentaires" déjà présentes.

Par exemple (et ces chiffres ne sont pas exacts, j'invente 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 chaîne, elle "fera confiance" à une précision plus précise. Ne arrondissez donc pas aussi tôt et vous verrez les "chiffres supplémentaires" qui étaient déjà là, mais caché de toi.

Lorsque vous convertissez une chaîne en chaîne, vous vous retrouvez avec une valeur double qui est plus proche de la valeur de la chaîne que ne l'était le float d'origine - mais ce n'est que bon si vous croyez vraiment que la valeur de chaîne est ce que vous voulais vraiment.

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

108
Jon Skeet

Je trouve la conversion à la représentation binaire plus facile à saisir 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 float est étendu au double en ajoutant des 0 à la fin, mais que la double représentation de 0.27 est "plus précise", d'où le problème.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
33
Brecht Yperman

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

Combien de chiffres doivent être imprimés pour la fraction […]? Il doit y avoir au moins un chiffre pour représenter la partie décimale, et au-delà, autant, mais seulement autant, plus les chiffres nécessaires pour distinguer uniquement la valeur d'argument des valeurs adjacentes de type float. Supposons que x soit la valeur mathématique exacte représentée par la représentation décimale produite par cette méthode pour un argument fini non nul f. Alors f doit être la valeur float la plus proche de x; ou, si deux valeurs flottantes sont également proches de x, alors f doit être l'une d'elles et le bit le moins significatif de la signification de f doit être 0.

24
erickson

J'ai rencontré ce problème aujourd'hui et je n'ai pas pu utiliser le refactor de 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 appeler doubleResult.doubleValue () renvoie correctement 5623.23

Mais je ne suis pas tout à fait sûr que ce soit une solution correcte.

13
Andrew

Utilisez un BigDecimal au lieu de float/double. De nombreux nombres ne peuvent pas être représentés sous forme de virgule flottante binaire (par exemple, 0.1). Vous devez donc toujours arrondir le résultat avec une précision connue ou utiliser BigDecimal.

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

7
Aaron Digulla

J'ai trouvé la solution suivante:

public static Double getFloatAsDouble(Float fValue) {
    return Double.valueOf(fValue.toString());
}

Si vous utilisez float et double au lieu de Float et Double utilisez les éléments suivants:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
5
GSD.Aaz

Les flotteurs, par nature, sont imprécis et ont toujours des "problèmes" arrondis. Si la précision est importante, vous pouvez envisager de refactoriser votre application pour utiliser Decimal ou BigDecimal. 

Oui, les flottants sont plus rapides en calcul que les décimales en raison de la prise en charge par le processeur. Cependant, voulez-vous rapide ou précis?

1
NotMe

Pour plus d'informations, reportez-vous à la section Article 48 - Evitez les valeurs float et double lorsque des valeurs exactes sont requises, dans Effective Java 2nd edition de Joshua Bloch. Ce livre est bourré de bonnes choses et mérite vraiment le détour. 

0
mR_fr0g

Est-ce que ça marche?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;
0
AesmaDiv