javascript - español - tesis crowdfunding
Cálculo Financiero Preciso en JavaScript. ¿Qué son las Gotchas? (4)
Con el interés de crear código multiplataforma, me gustaría desarrollar una aplicación financiera simple en JavaScript. Los cálculos requeridos involucran interés compuesto y números decimales relativamente largos. Me gustaría saber qué errores debo evitar al usar JavaScript para hacer este tipo de matemática, ¡si es posible!
Escalar cada valor por 100 es la solución. Hacerlo a mano probablemente sea inútil, ya que puede encontrar bibliotecas que lo hagan por usted. Recomiendo Moneysafe, que ofrece una API funcional muy adecuada para aplicaciones ES6:
const { in$, $ } = require(''moneysafe'');
console.log(in$($(10.5) + $(.3)); // 10.8
https://github.com/ericelliott/moneysafe
Funciona tanto en Node.js como en el navegador.
No existe el cálculo financiero "preciso" debido a solo dos dígitos de fracción decimal, pero ese es un problema más general.
En JavaScript, puede escalar cada valor por 100 y usar Math.round()
cada vez que se produzca una fracción.
Puede usar un objeto para almacenar los números e incluir el redondeo en su método prototipos valueOf()
. Me gusta esto:
sys = require(''sys'');
var Money = function(amount) {
this.amount = amount;
}
Money.prototype.valueOf = function() {
return Math.round(this.amount*100)/100;
}
var m = new Money(50.42355446);
var n = new Money(30.342141);
sys.puts(m.amount + n.amount); //80.76569546
sys.puts(m+n); //80.76
De esta forma, cada vez que use un objeto Money, se representará redondeado a dos decimales. El valor no redondeado todavía es accesible a través de m.amount
.
Puede construir su propio algoritmo de redondeo en Money.prototype.valueOf()
, si lo desea.
Su problema proviene de la inexactitud en los cálculos de coma flotante. Si solo estás utilizando el redondeo para resolver esto, tendrás un error mayor al multiplicar y dividir.
La solución está abajo, una explicación a continuación:
Tendrás que pensar en las matemáticas detrás de esto para entenderlo. Los números reales como 1/3 no se pueden representar en matemáticas con valores decimales ya que son infinitos (por ejemplo, .333333333333333 ...). Algunos números en decimal no se pueden representar en binario correctamente. Por ejemplo, 0.1 no se puede representar en binario correctamente con un número limitado de dígitos.
Para una descripción más detallada mira aquí: http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Eche un vistazo a la implementación de la solución: http://floating-point-gui.de/languages/javascript/
Probablemente debería escalar sus valores decimales por 100, y representar todos los valores monetarios en centavos enteros. Esto es para evitar problemas con la lógica de punto flotante y la aritmética . No hay ningún tipo de datos decimales en JavaScript; el único tipo de datos numéricos es el punto flotante. Por lo tanto, se recomienda generalmente manejar dinero como 2550
centavos en lugar de 25.50
dólares.
Considere eso en JavaScript:
var result = 1.0 + 2.0; // (result === 3.0) returns true
Pero:
var result = 0.1 + 0.2; // (result === 0.3) returns false
La expresión 0.1 + 0.2 === 0.3
devuelve false
, pero afortunadamente la aritmética entera en coma flotante es exacta, por lo que los errores de representación decimales pueden evitarse escalando 1 .
Tenga en cuenta que si bien el conjunto de números reales es infinito, solo un número finito de ellos (18,437,736,874,454,810,627 para ser exactos) se puede representar exactamente mediante el formato de coma flotante de JavaScript. Por lo tanto, la representación de los otros números será una aproximación del número real 2 .
1 Douglas Crockford: JavaScript: Las partes buenas : Apéndice A: partes terribles (página 105) .
2 David Flanagan: JavaScript: The Definitive Guide, Cuarta edición : 3.1.3 Literales de coma flotante (página 31) .