jam*_*ack 115 javascript finance decimal
为了创建跨平台代码,我想用JavaScript开发一个简单的财务应用程序.所需的计算涉及复合利息和相对较长的十进制数.我想知道在使用JavaScript进行这种类型的数学时要避免哪些错误 - 如果有可能的话!
Dan*_*llo 102
您应该将小数值缩放100,并以整分美分表示所有货币值.这是为了避免浮点逻辑和算术问题.JavaScript中没有十进制数据类型 - 唯一的数字数据类型是浮点数.因此,通常建议将钱作为2550美分而不是25.50美元来处理.
在JavaScript中考虑一下:
var result = 1.0 + 2.0; // (result === 3.0) returns true
Run Code Online (Sandbox Code Playgroud)
但:
var result = 0.1 + 0.2; // (result === 0.3) returns false
Run Code Online (Sandbox Code Playgroud)
表达式0.1 + 0.2 === 0.3返回false,但幸运的是浮点中的整数运算是精确的,因此通过缩放1可以避免十进制表示错误.
请注意,虽然实数的集合是无限的,但只有有限数量的它们(准确地说是18,437,736,874,454,810,627)可以用JavaScript浮点格式精确表示.因此,其他数字的表示将是实际数字2的近似值.
1 Douglas Crockford:JavaScript:好的部分:附录A - 可怕的部分(第105页).
2 David Flanagan:JavaScript:The Definitive Guide,Fourth Edition:3.1.3 Floating-Point Literals(第31页).
Fra*_*tto 15
将每个值缩放100是解决方案.手工操作可能没用,因为您可以找到为您执行此操作的库.我推荐moneysafe,它提供了一个非常适合ES6应用的功能API:
const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8
Run Code Online (Sandbox Code Playgroud)
https://github.com/ericelliott/moneysafe
在Node.js和浏览器中都可以使用.
mar*_*gti 14
不幸的是,到目前为止所有的答案都忽略了这样一个事实:并非所有货币都有 100 个子单位(例如,分是美元 (USD) 的子单位)。伊拉克第纳尔 (IQD) 等货币有 1000 个子单位:伊拉克第纳尔有 1000 费尔。日元 (JPY) 没有子单位。因此“乘以 100 进行整数运算”并不总是正确的答案。
此外,对于货币计算,您还需要跟踪货币。您无法将美元 (USD) 添加到印度卢比 (INR)(未先将其中一种兑换成另一种)。
JavaScript 的整数数据类型可以表示的最大数量也有限制。
在货币计算中,您还必须记住,货币的精度是有限的(通常为 0-3 个小数点),并且需要以特定方式进行舍入(例如,“正常”舍入与银行家舍入)。要执行的舍入类型也可能因司法管辖区/货币而异。
How to handle Money in javascript对相关要点有很好的讨论。
在我的搜索中,我发现dinero.js库可以解决许多有关货币计算的问题。尚未在生产系统中使用它,因此无法对此给出明智的意见。
没有"精确"的财务计算这样的事情,因为只有两个小数位数,但这是一个更普遍的问题.
在JavaScript中,您可以将每个值缩放100,并且Math.round()每次都可以使用分数.
您可以使用对象来存储数字,并在其原型valueOf()方法中包含舍入.像这样:
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
Run Code Online (Sandbox Code Playgroud)
这样,每次使用Money对象时,它将被表示为四舍五入到两位小数.仍然可以访问未包含的值m.amount.
Money.prototype.valueOf()如果您愿意,可以将自己的舍入算法构建到其中.
您的问题源于浮点计算的不准确。如果您只是使用舍入来解决这个问题,那么在乘法和除法时就会产生更大的误差。
解决办法如下,解释如下:
您需要考虑其背后的数学才能理解它。像 1/3 这样的实数不能在数学中用十进制值表示,因为它们是无穷无尽的(例如 - .333333333333333 ...)。一些十进制数无法正确地用二进制表示。例如,0.1无法用有限的位数正确地用二进制表示。
有关更详细的描述,请参见此处:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
看看解决方案的实现: http://floating-point-gui.de/languages/javascript/