¿Cuál es la mejor manera de manejar los problemas de coma flotante con cálculos financieros en JavaScript?

Nunca use números de coma flotante para cálculos financieros. Como ha observado, esto lleva a problemas de redondeo porque la mayoría de las fracciones decimales como [math] 0.1_ {10} = 0.00011001100110011 \ dots_2 [/ math] no tienen una representación finita exacta en binario.

En cambio, solo represente todos los valores monetarios utilizando un número entero de las unidades relevantes más pequeñas de la moneda , por ejemplo, centavos. No necesita una biblioteca especial para esto. Se garantiza que los números JavaScript simples puedan representar exactamente cada número entero entre [matemática] -2 ^ {53} [/ matemática] y [matemática] 2 ^ {53} [/ matemática] y [matemática] 2 ^ {53} [/ matemáticas] centavos es de unos nueve quintillones de dólares.

(No vuelva a dividir entre 100 y convierta a dólares, como en el ejemplo que dio. Simplemente deje todo en centavos).

Yo personalmente manejo todos los cálculos con doble (o flotante) e imprimo el número financiero en formato con toFixed(2) . Si tiene que redondear a los 5 centavos más cercanos, puede multiplicar por 0.05 redondear y dividir por 0.05 ( (Math.round(x *0.05) /0.05).toFixed(2) ).

De esta manera, el programa solo conoce el doble y el usuario solo ve decimales. Lo único que debe tener en cuenta es que x == y para el usuario significa Math.abs(xy)<0.01 para el programa.

Este hábito se remonta al antiguo tiempo FORTRAN / COBOL, cuando los tipos decimales florecían en el campo frente al cual la fecha / hora / marca de tiempo de PHP parece una pequeña parte bien organizada de la teoría de tipos.

Todo va bien, sobre-aprox cancelando por debajo de aprox, incluyendo dividir un año en 356.2422 días con precisión astronómica y una cantidad anual en 12 (que da un número infinito de decimales en la base 10, en la base 2 y en la base 16). Si tuviera que complacer a su contador, y sumando 12 veces su salario anual redondeado dividido por 12 (primero / 12 luego redondeado), puede ver una diferencia del orden de 0.06. Si bien, si permite que su salario mensual sea el doble internamente y lo redondee solo antes de imprimir, el error debería ser del orden de 15 dígitos mejor, es decir, deberá agregar 1'000'000'000'000 ' 000 años para ver la diferencia.

Siempre que puedas, nunca uses punto flotante. A menos que tenga un conocimiento íntimo de cómo se implementa el punto flotante tanto en su lenguaje de programación como en su plataforma de hardware, es probable que esté buscando problemas.

Para la mayoría de los problemas, todo lo que necesita hacer es cambiar la forma en que representa sus números. Por ejemplo, si estoy escribiendo un programa para tratar con dinero, siempre multiplico el monto en efectivo por 100. Ahora, el lugar de las decenas y unidades representa el cambio, el lugar de las centenas representa billetes de dólar, etc. No se permite ningún valor por debajo de 0.

Al hacer esto, evitará errores que hayan causado fallas catastróficas en el pasado. Por favor, busque la falla en Dhahran para ver un ejemplo.

Esto depende de qué tan grandes sean los montos, cuántos números trabaje a la vez, si necesita lidiar con fracciones como las tasas de interés, si necesita realizar conversiones de divisas y cuántos dígitos significativos tiene su número entero disponible y punto flotante tipos de datos permitidos.

Por ejemplo, si todo con lo que tiene que trabajar son flotantes de 3 bytes y 1 byte de precisión simple, y expresa sus números en centavos para evitar el redondeo de punto flotante, comenzará a perder precisión antes de que su suma alcance los $ 100,000. Mejor que usar un punto flotante de precisión infinita o una biblioteca BCD bigint.

En javascript, generalmente tendrá flotadores de 64 bits que, para los cálculos en centavos, comenzarán a perder precisión en alrededor de un billón de dólares, rupias, yenes, etc. (totalmente posible de encontrar si está haciendo cálculos financieros a escala de PIB)

Aquí hay algunos consejos:

  • hacer operaciones a nivel de centavo (es decir, en lugar de 0.1 $ tendría 10 centavos)
  • si no puede evitar fp de ninguna manera, utilice un tipo de datos especial, por ejemplo, el tipo BigDecimal que se transfirió de Java a JS: iriscouch / bigdecimal.js
  • no compares usando “==”. Por ejemplo, no verifique si (0.1 + 0.2) == 0.3, porque devolverá falso. En cambio, compara los abdominales. valor de su diferencia si es menor que la máquina épsilon

Utilice los consejos de Anders Kaseorg. Si DEBE usar coma flotante, use una biblioteca de punto flotante de precisión arbitraria (este es un buen punto de partida Lista de software aritmético de precisión arbitraria) por ejemplo.

Javascript solo tiene números de coma flotante, por lo que lo mejor que puede hacer es mantener sus números en términos de centavos todo el tiempo y luego solo convertirlos a dólares cuando vaya a mostrar los números. Cualquier cosa que esté debajo del decimal es aproximaciones, por lo que es más seguro evitarlo todo junto.

El tipo BigDecimal de Java es probablemente la mejor opción. Almacena cada dígito individualmente (o, para ser pedante, como “mordiscos” en una matriz de bytes) para que las operaciones se realicen usando matemática entera. Sin embargo, el tipo puede ser un poco grande.