Convertir flotador a doble sin perder precisión


Tengo un flotador primitivo y necesito como un doble primitivo. Simplemente lanzar el flotador al doble me da una precisión extra rara. Por ejemplo:

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

Sin embargo, si en lugar de lanzar, saco el flotador como una cadena, y analizo la cadena como un doble, obtengo lo que quiero:

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

¿Hay una mejor manera que ir a la cadena y volver?

Author: plinth, 2009-05-27

10 answers

No es que estés en realidad obteniendo una precisión extra - es que el flotador no representaba con precisión el número que estabas buscando originalmente. El doble es representando el flotador original con precisión; toString está mostrando los datos "extra" que ya estaban presentes.

Por ejemplo (y estos números no son correctos, solo estoy inventando cosas) soporte que tenías:

float f = 0.1F;
double d = f;

Entonces el valor de f podría ser exactamente 0.100000234523. d tendrá exactamente el el mismo valor, pero cuando lo conviertas en una cadena, "confiará" en que es preciso con una precisión mayor, por lo que no se redondeará tan pronto, y verá los "dígitos adicionales" que ya estaban allí, pero ocultos para usted.

Cuando se convierte a una cadena y viceversa, se termina con un valor doble que está más cerca del valor de la cadena que el flotador original, pero eso solo es bueno si realmente cree que el valor de la cadena es lo que realmente quería.

¿Estás seguro que float / double son los tipos apropiados para usar aquí en lugar de BigDecimal? Si está tratando de utilizar números que tienen valores decimales precisos (por ejemplo, dinero), entonces BigDecimal es un tipo más apropiado IMO.

 106
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-03 22:44:29

Encuentro la conversión a la representación binaria más fácil de entender este problema.

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

Puede ver que el flotador se expande al doble agregando 0s al final, pero que la doble representación de 0.27 es 'más precisa', de ahí el problema.

   111110100010100011110101110001
11111111010001010001111010111000100000000000000000000000000000
11111111010001010001111010111000010100011110101110000101001000
 32
Author: Brecht Yperman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-04-09 21:22:45

Esto se debe al contrato de Float.toString(float), que dice en parte:

Para cuántos dígitos se deben imprimir la parte fraccionaria [...]? Alli debe tener al menos un dígito para representar la parte fraccionaria, y más allá de eso, como muchos, pero solo como muchos, más dígitos que se necesitan para distinguir el valor del argumento de valores adyacentes de tipo float. Que es, supongamos que x es el exacto valor matemático representado por el decimal representación producida por este método para un distinto de cero finito argumento f. Entonces f debe ser el flotador valor más cercano a x; o, si dos flotadores los valores son igualmente cercanos a x, luego f debe ser uno de ellos y el menos parte significativa de la significand de f debe ser 0.

 23
Author: erickson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-27 14:56:04

Me he encontrado con este problema hoy y no pude usar refactor a BigDecimal, porque el proyecto es realmente enorme. Sin embargo, encontré solución usando

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

Y esto funciona.

Tenga en cuenta que llamar resultado.doubleValue () devuelve 5623.22998046875

Pero llamando a doubleResult.doubleValue () devuelve correctamente 5623.23

Pero no estoy del todo seguro si es una solución correcta.

 14
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-02 12:24:55

Utilice un BigDecimal en lugar de float/double. Hay muchos números que no se pueden representar como punto flotante binario (por ejemplo, 0.1). Por lo tanto, siempre debe redondear el resultado a una precisión conocida o usar BigDecimal.

Véase http://en.wikipedia.org/wiki/Floating_point para más información.

 6
Author: Aaron Digulla,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-16 13:03:38

Encontré la siguiente solución:

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

Si se uso float y doble en lugar de Float y Doble utilice el siguiente:

public static double getFloatAsDouble(float value) {
    return Double.valueOf(Float.valueOf(value).toString()).doubleValue();
}
 4
Author: GSD.Aaz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-25 16:20:59

Los flotadores, por naturaleza, son imprecisos y siempre tienen "problemas"de redondeo. Si la precisión es importante, entonces podría considerar refactorizar su aplicación para usar Decimal o BigDecimal.

Sí, los flotadores son computacionalmente más rápidos que los decimales debido al soporte on processor. Sin embargo, ¿quieres rápido o preciso?

 1
Author: NotMe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-27 14:55:36

Para información, esto viene bajo el Ítem 48 - Evitar float y double cuando se requieren valores exactos, de Effective Java 2nd edition por Joshua Bloch. Este libro está repleto de cosas buenas y definitivamente vale la pena echarle un vistazo.

 0
Author: mR_fr0g,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-27 15:05:43

¿Funciona esto?

float flt = 145.664454;

Double dbl = 0.0;
dbl += flt;
 0
Author: AesmaDiv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-04 21:31:24

Si necesitas hacerlo con envoltorios:

Double d = new Float(123f).doubleValue();
 0
Author: Dzmitry Atkayey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-27 13:00:39