2012-10-05 66 views
9

puedo tener algunos problemas con la precisión float de JavaCuál es la manera correcta de parseFloat en Java

 Float.parseFloat("0.0065") - 0.001 // 0.0055000000134110451 
     new Float("0.027") - 0.001   // 0.02600000000700354575 
     Float.valueOf("0.074") - 0.001  // 0.07399999999999999999 

no sólo tengo un problema con Float sino también con Double.

Puede alguien explicar lo que está sucediendo detrás de las escenas, y cómo podemos obtener un número exacto? ¿Cuál sería la forma correcta de manejar esto cuando se trata de estos problemas?

Respuesta

28

El problema es simplemente que float tiene una precisión finita; no puede representar 0.0065 exactamente. (Lo mismo es cierto de double, por supuesto: que tiene una mayor precisión, pero todavía finito.)

Un problema adicional, que hace que el problema anterior más evidente, es que 0.001 es una double en lugar de un float, por lo que su float se promociona a double para realizar la resta y, por supuesto, en ese momento el sistema no tiene manera de recuperar la precisión faltante que doublepudo haber representado para empezar. Para hacer frente a esto, usted escribiría:

float f = Float.parseFloat("0.0065") - 0.001f; 

usando 0.001f en lugar de 0.001.

+7

+1 por mencionar que '0.001' es un' doble'. –

+0

diciendo que "float se está promocionando a un doble" ¿significa que el compilador lo ha duplicado internamente? ¿Promueve que float se duplique incondicionalmente cada vez que lo usemos con el doble para el cálculo? – peter

+0

@ user1389813: Sí. De acuerdo con [§5.6.2 "Promoción binaria numérica" ​​de * La especificación del lenguaje Java * (Java SE 7 Edition)] (http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6 .2), hay varios operadores numéricos, incluido '-', con la propiedad de que ambos operandos se convertirán al mismo tipo para realizar el cálculo. Una de las reglas de conversión es: "Si cualquier operando es del tipo' double', el otro se convierte en 'double'." – ruakh

5

Estás obteniendo los resultados correctos. No hay tal float como 0.027 exactamente, ni existe tal double. Usted siempre obtener estos errores si utiliza float o double.

float y double se almacenan como fracciones binarias: algo así como 1/2 + 1/4 + 1/16 ... No se puede obtener todos los valores decimales que se almacenan exactamente como fracciones binarias de precisión finita. Simplemente no es matemáticamente posible.

La única alternativa es utilizar BigDecimal, que se puede utilizar para obtener valores exactos decimales.

+0

Así que me pregunto cómo podemos hacer frente a algo así como Float.valueOf ("0,074") // 0.07399999999999999999 y luego multiplicado por 1000 a eso. ¿Regresaré 74? o necesito hacer un rodeo todo el tiempo? – peter

+0

@ user1389813, si usa BigDecimal puede aliviar la mayoría de esas preocupaciones. – kurtzbot

+0

No puedes multiplicar por 1000 y obtener la respuesta correcta. (Puede obtener la respuesta correcta, dependiendo del comportamiento de redondeo, o puede que no). Debe usar 'BigDecimal' si no desea estos problemas. –

1

Para resumir, si usted requiere el uso arbitrario de precisión no BIGDECIMAL flotar o doble. Verá todo tipo de problemas de redondeo de esta naturaleza con flotación.

Como acotación al margen tener mucho cuidado de no usar el flotador/doble constructor de BigDecimal porque va a tener el mismo problema. Use el constructor String en su lugar.

1

El punto flotante no puede representar con exactitud los números decimales. Si necesita una representación precisa de un número en Java, debe usar java.math.clase BigDecimal:

BigDecimal d = new BigDecimal("0.0065"); 
4

Desde el Java Tutorials page on Primitive Data Types:

Un literal de coma flotante es de tipo float si termina con la letra F o f; de lo contrario, su tipo es doble y opcionalmente puede finalizar con la letra D o d.

Creo que tus literales (0.001) son dobles y restas dobles de carrozas.

Tal vez puedas probar:

System.out.println((0.0065F - 0.001D)); // 0.005500000134110451 
System.out.println((0.0065F - 0.001F)); // 0.0055 

... y obtendrá:

0.005500000134110451 
0.0055 

por lo que añadir F sufijos a sus literales y usted debe obtener mejores resultados:

Float.parseFloat("0.0065") - 0.001F 
new Float("0.027") - 0.001F 
Float.valueOf("0.074") - 0.001F 
+0

me pregunto qué pasará con System.out.println ((0.0065F - 0.001D)); ? – peter

+0

Obtiene '0.005500000134110451'; intentalo. Lo edité para aclararlo. –

+0

Sí, lo probé, pero ¿por qué termina así cuando D-F así como F-D? – peter

3

Convertiría tu flotador a una cadena y luego usaría BigDecimal.

This enlace explica bien

new BigDecimal(String.valueOf(yourDoubleValue)); 

no uso el doble constructor BigDecimal aunque como usted seguirá recibiendo errores

Cuestiones relacionadas