JavaScript中的精确财务计算,Gotchas是什么?

h4cxqtbf  于 2023-05-21  发布在  Java
关注(0)|答案(9)|浏览(123)

为了创建跨平台代码,我想用JavaScript开发一个简单的金融应用程序。所需的计算涉及复利和相对较长的十进制数。我想知道在使用JavaScript进行这种类型的数学运算时应该避免哪些错误-如果可能的话!

kmb7vmvb

kmb7vmvb1#

您可能应该将十进制值缩放100,并以整美分表示所有货币值。这是为了避免problems with floating-point logic and arithmetic。JavaScript中没有decimal数据类型-唯一的数字数据类型是浮点型。因此,通常建议将货币处理为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,但幸运的是,浮点数中的整数运算是精确的,因此可以通过scaling 1避免十进制表示错误。
请注意,虽然真实的集是无限的,但只有有限个(确切地说是18,437,736,874,454,810,627)可以用JavaScript浮点格式精确表示。因此,其他数的表示将是实际数2的近似值。
1道格拉斯·克罗克福德:JavaScript:好的部分:附录A -可怕的部分(第105页)。
2大卫·弗拉纳根:JavaScript: The Definitive Guide, Fourth Edition: 3.1.3 Floating-Point Literals (page 31)

zkure5ic

zkure5ic2#

将每个值缩放100是解决方案。手工做可能是无用的,因为你可以找到为你做这件事的库。我推荐moneysafe,它提供了一个非常适合ES6应用程序的函数式API:

const { in$, $ } = require('moneysafe');
console.log(in$($(10.5) + $(.3)); // 10.8

https://github.com/ericelliott/moneysafe
在Node.js和浏览器中都可以使用。

qvtsj1bj

qvtsj1bj3#

没有所谓的“精确”的财务计算,因为只有两个小数位,但这是一个更普遍的问题。
在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-object时,它都将被表示为四舍五入到两位小数。未舍入的值仍然可以通过m.amount访问。
如果愿意,可以在Money.prototype.valueOf()中构建自己的舍入算法。

neskvpey

neskvpey4#

不幸的是,到目前为止,所有的答案都忽略了一个事实,即并非所有货币都有100个子单位(例如,美分是美元(USD)的子单位)。像伊拉克第纳尔(IQD)这样的货币有1000个子单位:伊拉克第纳尔等于1000费尔。日元(JPY)没有子单位。所以“乘以100做整数运算”并不总是正确的答案。
此外,对于货币计算,您还需要跟踪货币。您不能将美元(USD)添加到印度卢比(INR)(没有先将一个转换为另一个)。
JavaScript的整数数据类型所能表示的最大数量也有限制。
在货币计算中,您还必须记住,货币具有有限的精度(通常为0-3个小数点),四舍五入需要以特定的方式进行(例如,“正常”四舍五入vs.银行家四舍五入)。要执行的四舍五入类型也可能因管辖区/货币而异。
How to handle money in javascript对相关点有很好的讨论。
在我的搜索中,我找到了dinero.js库。还没有在生产系统中使用它,所以不能给予一个明智的意见。

swvgeqrz

swvgeqrz5#

use decimaljs...它是一个非常好的库,解决了问题的一个苛刻的部分...
在你所有行动中使用它。
https://github.com/MikeMcl/decimal.js/

jv4diomz

jv4diomz6#

您的问题源于浮点计算的不准确性。如果你只是使用舍入来解决这个问题,你会有更大的错误,当你乘和除。
解决方案如下,解释如下:
你需要思考这背后的数学来理解它。像1/3这样的真实的在数学中不能用十进制值来表示,因为它们是无穷的(例如- .3333333333333...)。十进制中的某些数字不能正确地用二进制表示。例如,0.1不能用有限的位数正确地以二进制表示。
更多详细描述请看这里:http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

看看解决方案实现:http://floating-point-gui.de/languages/javascript/

fumotvh3

fumotvh37#

由于其编码的二进制性质,一些十进制数不能以完美的准确度表示。举个例子

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);

Avoiding Problems with Decimal Math in JavaScript
有一个专门的图书馆为财务计算与伟大的文件。Finance.js

dm7nw8vv

dm7nw8vv8#

使用此代码进行货币计算和两位数的整数。

<!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>
23c0lvtd

23c0lvtd9#

在这里,我们使用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上的任何人。

相关问题