Hola usuario de nuestro sitio web, hallamos la respuesta a tu interrogante, continúa leyendo y la obtendrás aquí.
Solución:
Porque los flotantes y los dobles no pueden representar con precisión los múltiplos de base 10 que usamos para el dinero. Este problema no es solo para Java, es para cualquier lenguaje de programación que use tipos de punto flotante base 2.
En base 10, puedes escribir 10.25 como 1025 * 10-2 (un número entero por una potencia de 10). Los números de punto flotante IEEE-754 son diferentes, pero una forma muy sencilla de pensar en ellos es multiplicarlos por una potencia de dos. Por ejemplo, podría estar mirando 164 * 2-4 (un número entero por una potencia de dos), que también es igual a 10,25. No es así como se representan los números en la memoria, pero las implicaciones matemáticas son las mismas.
Incluso en base 10, esta notación no puede representar con precisión la mayoría de las fracciones simples. Por ejemplo, no puedes representar 1/3: la representación decimal se repite (0,3333…), por lo que no hay un número entero finito que puedas multiplicar por una potencia de 10 para obtener 1/3. Podría decidirse por una secuencia larga de 3 y un exponente pequeño, como 333333333 * 10-10pero no es exacto: si lo multiplicas por 3, no obtendrás 1.
Sin embargo, para el propósito de contar dinero, al menos para países cuyo dinero se valora dentro de un orden de magnitud del dólar estadounidense, por lo general todo lo que necesita es poder almacenar múltiplos de 10-2por lo que realmente no importa que 1/3 no se pueda representar.
El problema con los flotadores y los dobles es que el gran mayoría de números similares al dinero no tienen una representación exacta como un número entero por una potencia de 2. De hecho, los únicos múltiplos de 0.01 entre 0 y 1 (que son significativos cuando se trata de dinero porque son centavos enteros) que pueden representarse exactamente como un número de punto flotante binario IEEE-754 son 0, 0,25, 0,5, 0,75 y 1. Todos los demás están equivocados por una pequeña cantidad. Como analogía con el ejemplo de 0,333333, si toma el valor de coma flotante de 0,1 y lo multiplica por 10, no obtendrá 1.
Representar el dinero como un double
o float
probablemente se verá bien al principio ya que el software redondea los pequeños errores, pero a medida que realiza más sumas, restas, multiplicaciones y divisiones en números inexactos, los errores se agravarán y terminará con valores que visiblemente no son precisos. Esto hace que los flotantes y los dobles sean inadecuados para manejar dinero, donde se requiere una precisión perfecta para múltiplos de potencias de base 10.
Una solución que funciona en casi cualquier idioma es usar números enteros y contar centavos. Por ejemplo, 1025 sería $10,25. Varios idiomas también tienen tipos incorporados para manejar el dinero. Entre otros, Java tiene la BigDecimal
clase, y C# tiene la decimal
escribe.
De Bloch, J., Java efectivo, 2.ª ed., artículo 48:
los
float
ydouble
tipos son particularmente inadecuados para los cálculos monetarios porque es imposible representar 0.1 (o cualquier otra potencia negativa de diez) como unfloat
o
double
exactamente.Por ejemplo, suponga que tiene $1.03 y gasta 42c. ¿Cuánto dinero te queda?
System.out.println(1.03 - .42);
imprime
0.6100000000000001
.La forma correcta de resolver este problema es usar
BigDecimal
,int
olong
para cálculos monetarios.
Aunque BigDecimal
tiene algunas advertencias (consulte la respuesta actualmente aceptada).
Esto no es una cuestión de exactitud, ni es una cuestión de precisión. Se trata de cumplir con las expectativas de los humanos que usan la base 10 para los cálculos en lugar de la base 2. Por ejemplo, usar dobles para los cálculos financieros no produce respuestas que sean “incorrectas” en un sentido matemático, pero puede producir respuestas que son no es lo que se espera en un sentido financiero.
Incluso si redondea sus resultados en el último minuto antes de la salida, ocasionalmente puede obtener un resultado utilizando dobles que no coincida con las expectativas.
Usando una calculadora, o calculando los resultados a mano, 1.40 * 165 = 231 exactamente. Sin embargo, internamente usando dobles, en mi entorno de compilador/sistema operativo, se almacena como un número binario cercano a 230.99999… así que si truncas el número, obtienes 230 en lugar de 231. Puedes razonar que redondear en lugar de truncar sería han dado el resultado deseado de 231. Eso es true, pero el redondeo siempre implica truncamiento. Independientemente de la técnica de redondeo que utilice, aún existen condiciones de contorno como esta que se redondearán hacia abajo cuando espera que se redondeen hacia arriba. Son tan raros que a menudo no se encuentran a través de pruebas u observaciones casuales. Es posible que deba escribir código para buscar ejemplos que ilustren los resultados que no se comportan como se esperaba.
Suponga que desea redondear algo al centavo más cercano. Así que toma su resultado final, multiplica por 100, suma 0.5, trunca y luego divide el resultado por 100 para volver a los centavos. Si el número interno que almacenó fue 3,46499999… en lugar de 3,465, obtendrá 3,46 en lugar de 3,47 cuando redondee el número al centavo más cercano. Pero sus cálculos en base 10 pueden haber indicado que la respuesta debería ser exactamente 3,465, que claramente debería redondearse a 3,47, no a 3,46. Este tipo de cosas suceden ocasionalmente en la vida real cuando usas dobles para cálculos financieros. Es raro, por lo que a menudo pasa desapercibido como un problema, pero sucede.
Si usa base 10 para sus cálculos internos en lugar de dobles, las respuestas siempre son exactamente lo que esperan los humanos, suponiendo que no haya otros errores en su código.
valoraciones y reseñas
Al final de todo puedes encontrar las críticas de otros programadores, tú incluso eres capaz mostrar el tuyo si te gusta.