¿Qué pasa con este simple cálculo “doble”?

¿Qué pasa con este simple cálculo ‘doble’ en Java?

Sé que algunos números decimales no se pueden representar correctamente en los formatos float / double binary, pero con la variable d3, java puede almacenar y mostrar 2.64 sin problemas.

double d1 = 4.64; double d2 = 2.0; double d3 = 2.64; double d4 = d1 - d2; System.out.println("d1 : " + d1); System.out.println("d2 : " + d2); System.out.println("d3 : " + d3); System.out.println("d4 : " + d4); System.out.println("d1 - d2 : " + (d1 - d2)); 

Responder,

 d1 : 4.64 d2 : 2.0 d3 : 2.64 d4 : 2.6399999999999997 d1 - d2 : 2.6399999999999997 

El problema

En el binario 2.64 es 10.10100011110101110000101000111101 recurrente, en otras palabras, no es exactamente representable en binario, de ahí el pequeño error. Java está siendo amable con usted con d3, pero tan pronto como los cálculos reales estén involucrados tiene que recurrir a la representación real.

Calculadora Binaria

Además:

 2.64= 10.10100011110101110000101000111101 4.64=100.1010001111010111000010100011110 

Ahora, aunque el .64 es el mismo en ambos casos, se mantiene con diferentes precisiones porque el 4 = 100 consume más de las cifras dobles significativas que 2 = 10, por lo que cuando dices 4.64-2.0 y 2.64 el .64 se representa con un error de redondeo diferente en ambos casos, esta información perdida no se puede recuperar para la respuesta final.

NB No estoy usando el double número de cifras significativas aquí, simplemente sea lo que sea que produzca la calculadora binaria, sin embargo, el efecto es el mismo sea cual sea el número de cifras significativas

Nunca suponga que los valores dobles son exactos (aunque sus imprecisiones son microscópicas y se deben a que ciertos números no se pueden express exactamente en binario).

Los números de punto flotante no son exactos, pero solo desde un punto de vista decimal

Si bien siempre se debe esperar que los dobles tengan pequeños errores en los últimos lugares decimales, sería incorrecto pensar que las representaciones binarias son “malas” o peor que las decimales.

Todos estamos acostumbrados a que ciertos números (como 1/3, por ejemplo) no sean exactamente representables en decimales y aceptamos que ese número terminará en 0.333333333333 en lugar del verdadero (que no puedo escribir sin espacio infinito); es dentro de ese contexto que los números binarios no se pueden express exactamente. 1/10 es un número que no se puede express exactamente en binario; esto nos sorprende solo porque estamos acostumbrados al decimal

d1 – d2 devuelve el resultado exacto de la aritmética de flotación binaria y es 2.6399999999999997, por lo que se imprime. Si desea redondearlo, puede hacerlo durante la impresión

 System.out.printf("d1 - d2 : %.2f", d4); 

o con Commons-Math

 d4 = Precision.round(d4, 2); 

Es porque los errores en las representaciones internas de 4.64 y 2.0 se combinan de manera constructiva (lo que significa que crean un error mayor).

Técnicamente hablando, 2.64 tampoco se almacena exactamente. Sin embargo, hay una representación particular que corresponde a 2.64. Sin embargo, piense en el hecho de que 4.64 y 2.0 no están almacenados exactamente. Los errores en 4.64 y 2.0 se combinan para producir un error aún mayor, uno lo suficientemente grande como para que su sustracción no proporcione la representación de 2.64.

La respuesta está desactivada por 3 * 10 ^ -16. Para dar un ejemplo de cómo puede suceder eso, pretendamos que la representación para 4.64 es 2 * 10 ^ -16 demasiado pequeña y la representación para 2.0 es 1 * 10 ^ -16 demasiado grande. Entonces obtendrías

 (4.64 - 2*10^-16) - (2.0 + 1*10^-16) = 2.64 - 3*10^-16 

Entonces, cuando se realiza el cálculo, los dos errores se combinan para crear un error aún mayor. Pero si la representación de 2.64 solo está desactivada en 1 * 10 ^ -16, la computadora no consideraría igual a 2.64.

También es posible que 4.64 solo tenga un error mayor que 2.64, incluso si 2.0 no tiene ningún error. Si la representación de 4.64 es 3 * 10 ^ -16 demasiado pequeña, obtienes lo mismo:

 (4.64 - 3*10^-16) - 2.0 = 2.64 - 3*10^-16 

Nuevamente, si la representación de 2.64 está solo fuera de 1 * 10 ^ -16, entonces este resultado no se consideraría igual a 2.64.

No sé los errores exactos en las representaciones reales, pero algo similar está sucediendo, solo con diferentes valores. Espero que tenga sentido. Siéntase libre de pedir una aclaración.

Principalmente por el hecho de que el doble es un punto flotante IEEE 754 de 64 bits de doble precisión. No es para mantener valores decimales exactos. Es por eso que los dobles no son recomendados para cálculos exactos. Use el constructor String de BigDecimal en su lugar, como:

 new BigDecimal("2.64") 

No hay nada malo con eso Pero intenta usar BigDecimal

http://docs.oracle.com/javase/6/docs/api/java/math/BigDecimal.html

Nota: el doble y el flotante se representan internamente como fracciones binarias de acuerdo con el estándar IEEE 754 y, por lo tanto, no pueden representar las fracciones decimales exactamente

Intereting Posts