JavaScript中精确的财务计算。 什么是陷阱?
为了创build跨平台的代码,我想开发一个简单的JavaScript应用程序。 所需的计算涉及复利和相对较长的十进制数。 我想知道在使用JavaScript来完成这种math运算时要避免哪些错误 – 如果可能的话!
您可能应该将您的小数值缩放100,并将所有货币值代表整个美分。 这是为了避免浮点逻辑和算术问题 。 JavaScript中没有十进制数据types – 唯一的数字数据types是浮点数。 因此,一般build议处理钱为2550
美分,而不是25.50
美元。
考虑到在JavaScript中:
var result = 1.0 + 2.0; // (result === 3.0) returns true
但:
var result = 0.1 + 0.2; // (result === 0.3) returns false
expression式0.1 + 0.2 === 0.3
返回false
,但幸运的是浮点数的整数运算是精确的,所以可以通过缩放1来避免十进制表示错误。
请注意,虽然实数集合是无限的,但只有有限数量(准确地说,是18,437,736,874,454,810,627)可以用JavaScript浮点格式来表示。 因此,其他数字的表示将是实际数字2的近似值。
1道格拉斯·克罗克福德: JavaScript:好的部分 :附录A – 可怕的部分(第105页) 。
2 David Flanagan: JavaScript:权威指南,第4版 :3.1.3浮点文字(第31页) 。
由于只有两位十进制小数位,所以没有“精确”的财务计算,但这是一个更普遍的问题。
在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
这样,每次使用Money对象时,它都将被表示为四舍五入到小数点后两位。 未m.amount
值仍然可以通过m.amount
访问。
如果你愿意的话,你可以用自己的四舍五入algorithm构buildMoney.prototype.valueOf()
。
将每个值缩放100是解决scheme。 这样做可能是无用的,因为你可以find为你做的那些库。 我推荐moneysafe,它提供了一个非常适合ES6应用程序的function性API:
const { in$, $ } = require('moneysafe'); console.log(in$($(10.5) + $(.3)); // 10.8
https://github.com/ericelliott/moneysafe
在Node.js和浏览器中都可以使用。
你的问题源于浮点计算的不准确。 如果你只是使用四舍五入来解决这个问题,那么当你乘数和除数时你将会有更大的错误。
解决办法如下,解释如下:
你需要考虑math背后的理解。 像1/3这样的实数不能用数字表示,因为它们是无穷的(例如 – .333333333333333 …)。 一些十进制数字不能用二进制正确表示。 例如,0.1不能用有限的数字位数以二进制正确表示。
有关更详细的说明,请看这里: http : //docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
看一下解决scheme的实现: http : //floating-point-gui.de/languages/javascript/