por ejemplo, con el dólar, nunca tiene una precisión de menos de $ 0.01
¿Oh enserio?
El antiguo problema de por qué no debe almacenar moneda como un número de coma flotante IEEE 754.
No dude en almacenar pulgadas en números de punto flotante IEEE 754 . Almacenan exactamente como cabría esperar.
No dude en almacenar cualquier cantidad de dinero en números de coma flotante IEEE 754 que pueda almacenar utilizando los ticks que dividen una regla en fracciones de pulgada.
¿Por qué? Porque cuando usa IEEE 754 , así es como lo almacena.
Lo que pasa con las pulgadas es que están divididas en mitades. Lo que pasa con la mayoría de los tipos de moneda es que están divididos en décimas (algunos tipos no lo son, pero centrémonos).
¡Esta diferencia no sería tan confusa, excepto que, para la mayoría de los lenguajes de programación, la entrada y salida de los números de coma flotante IEEE 754 se expresa en decimales! Lo cual es muy extraño porque no están almacenados en decimales.
Debido a esto, nunca puedes ver cómo los bits hacen cosas raras cuando le pides a la computadora que almacene 0.1
. Solo ves la rareza cuando haces cálculos matemáticos y tiene errores extraños.
Del efectivo java de Josh Bloch :
System.out.println(1.03 - .42);
Produce 0.6100000000000001
Lo más revelador de esto no es el 1
camino sentado a la derecha. Son los números extraños que tuvieron que usarse para obtenerlo. En lugar de usar el ejemplo más popular 0.1
, tenemos que usar un ejemplo que muestre el problema y evite el redondeo que lo ocultaría.
Por ejemplo, ¿por qué funciona esto?
System.out.println(.01 - .02);
Produce -0.01
Porque tuvimos suerte.
Odio los problemas que son difíciles de diagnosticar porque a veces tengo "suerte".
IEEE 754 simplemente no puede almacenar 0.1 con precisión. Pero si le pide que almacene 0.1 y luego le pida que imprima, mostrará 0.1 y pensará que todo está bien. No está bien, pero no puedes ver eso porque se está redondeando para volver a 0.1.
Algunas personas confunden a otros llamando a estas discrepancias redondeando los errores. No, estos no son errores de redondeo. El redondeo es hacer lo que se supone que debe hacer y convertir lo que no es un decimal en un decimal para que se pueda imprimir en la pantalla.
Pero esto oculta la falta de coincidencia entre cómo se muestra el número y cómo se almacena. El error no ocurrió cuando ocurrió el redondeo. Sucedió cuando decidió poner un número en un sistema que no puede almacenarlo con precisión y asumió que se estaba almacenando precisamente cuando no lo estaba.
Nadie espera que π se almacene con precisión en una calculadora y logran trabajar con ella perfectamente. Entonces, el problema ni siquiera es acerca de la precisión. Se trata de la precisión esperada. Las computadoras muestran una décima como 0.1
lo hacen nuestras calculadoras, por lo que esperamos que almacenen una décima perfectamente como lo hacen nuestras calculadoras. Ellos no. Lo cual es sorprendente, ya que las computadoras son más caras.
Déjame mostrarte el desajuste:
Observe que 1/2 y 0.5 se alinean perfectamente. Pero 0.1 simplemente no se alinea. Claro que puedes acercarte si sigues dividiendo por 2, pero nunca lo acertarás exactamente. Y necesitamos más y más bits cada vez que dividimos por 2. Por lo tanto, representar 0.1 con cualquier sistema que divide por 2 necesita un número infinito de bits. Mi disco duro no es tan grande.
Por lo tanto, IEEE 754 deja de intentarlo cuando se queda sin bits. Lo cual es bueno porque necesito espacio en mi disco duro para ... fotos familiares. No realmente. Fotos de familia. :PAG
De todos modos, lo que escribe y lo que ve son los decimales (a la derecha), pero lo que almacena son bicimales (a la izquierda). A veces esos son perfectamente iguales. A veces no lo son. A veces parece que son iguales cuando simplemente no lo son. Ese es el redondeo.
En particular, ¿qué necesitamos saber para poder almacenar valores en alguna moneda e imprimirlos?
Por favor, si está manejando mi dinero basado en decimales, no use flotadores o dobles.
Si está seguro de que cosas como décimas de centavos no estarán involucradas, simplemente almacene centavos. Si no lo está, averigüe cuál será la unidad más pequeña de esta moneda y úsela. Si no puede, use algo como BigDecimal .
Mi patrimonio neto probablemente siempre encajará en un entero de 64 bits, pero cosas como BigInteger funcionan bien para proyectos más grandes que eso. Son solo más lentos que los tipos nativos.
Descubrir cómo almacenarlo es solo la mitad del problema. Recuerde que también debe poder mostrarlo. Un buen diseño separará estas dos cosas. El verdadero problema con el uso de flotadores aquí es que esas dos cosas se mezclan.