出于创建跨平台代码的目的,我想用JavaScript开发一个简单的金融应用程序。所需的计算涉及复利和相对较长的小数。我想知道在使用JavaScript进行此类数学计算时应避免哪些错误——如果可能的话!
出于创建跨平台代码的目的,我想用JavaScript开发一个简单的金融应用程序。所需的计算涉及复利和相对较长的小数。我想知道在使用JavaScript进行此类数学计算时应避免哪些错误——如果可能的话!
你应该将小数值乘以100,并将所有货币价值表示为整份的美分。这是为了避免浮点逻辑和算术问题。在JavaScript中没有小数数据类型 - 唯一的数字数据类型是浮点数。因此,通常建议将钱款处理为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
表达式0.1 + 0.2 === 0.3
返回false
,但幸运的是,在浮点数中进行整数运算是精确的,因此可以通过缩放来避免小数表示误差1。1道格拉斯·克罗克福德:JavaScript语言精粹: 附录A - 糟糕的部分 (第105页)。
2大卫·弗拉纳根:JavaScript权威指南,第四版: 3.1.3 浮点字面量 (第31页)。
将每个值都乘以100是解决方案。手动操作可能是无用的,因为您可以找到为您完成此操作的库。我推荐使用moneysafe,它提供了适用于ES6应用程序的功能API:
const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8
https://github.com/ericelliott/moneysafe
可同时在Node.js和浏览器中使用。
in$, $
这些变量名对于那些没有使用过该软件包的人来说是含糊不清的。我知道这是Eric的选择来命名它们,但我仍然觉得这是一个足够严重的错误,以至于我可能会在导入/解构需要声明中重新命名它们。 - james_womack由于只有两个小数位,所以不存在“精确”的财务计算,但这是一个更普遍的问题。
在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
访问。
如果您愿意,可以在Money.prototype.valueOf()
中构建自己的舍入算法。
Money(0.1)
,JavaScript词法分析器从源代码中读取字符串“0.1”,然后将其转换为二进制浮点数,这样就会出现意外的四舍五入。问题在于_表示_(二进制 vs 十进制),而不是_精度_。 - mgd由于二进制编码的本质,一些十进制数无法被完全准确地表示。例如:
var money = 600.90;
var price = 200.30;
var total = price * 3;
// Outputs: false
console.log(money >= total);
// Outputs: 600.9000000000001
console.log(total);
如果你需要使用纯JavaScript,那么你需要考虑每个计算的解决方案。对于上面的代码,我们可以将小数转换为整数。
var money = 60090;
var price = 20030;
var total = price * 3;
// Outputs: true
console.log(money >= total);
// Outputs: 60090
console.log(total);
有一个专门用于财务计算的库,文档非常好。 Finance.js
<!DOCTYPE html>
<html>
<body>
<h1>JavaScript Variables</h1>
<p id="test1"></p>
<p id="test2"></p>
<p id="test3"></p>
<script>
function setDecimalPoint(num) {
if (isNaN(parseFloat(num)))
return 0;
else {
var Number = parseFloat(num);
var multiplicator = Math.pow(10, 2);
Number = parseFloat((Number * multiplicator).toFixed(2));
return (Math.round(Number) / multiplicator);
}
}
document.getElementById("test1").innerHTML = "Without our method O/P is: " + (655.93 * 9)/100;
document.getElementById("test2").innerHTML = "Calculator O/P: 59.0337, Our value is: " + setDecimalPoint((655.93 * 9)/100);
document.getElementById("test3").innerHTML = "Calculator O/P: 32.888.175, Our value is: " + setDecimalPoint(756.05 * 43.5);
</script>
</body>
</html>
在这里,我们使用JavaScript和Node.js在金融市场上交易真实货币。 我们设计了一个自定义的Decimal类,代表不可变的十进制数,并提供计算逻辑。
import { decimal, } from "@reiryoku/mida";
0.1 + 0.2; //= 0.30000000000000004
decimal(0.1).add(0.2); //= 0.3
decimal("0.1").add("0.2"); //= 0.3
如果你感到好奇,可以在这里查看定义https://github.com/Reiryoku-Technologies/Mida/blob/master/src/core/decimals/MidaDecimal.ts
该软件包可供任何人在npm上使用。
3000.57 + 0.11 === 3000.68
的结果是false
。 - Daniel Vassallo