在C ++中存储货币值的最佳方法

时间:2020-03-06 14:52:41  来源:igfitidea点击:

我知道,由于舍入错误,浮点数不适合存储货币值。有没有一种用C ++表示金钱的标准方法?

我看过Boost库,却一无所获。在Java中,似乎BigInteger是这样,但我找不到C ++中的等效项。我可以编写自己的货币课程,但如果经过测试,则不愿这样做。

解决方案

关于取整,这取决于业务需求。最安全的方法是存储具有所需精度的整数,并知道何时/如何应用舍入。

我建议我们为美分而不是美元保留一个变量。那应该消除舍入错误。以标准美元/美分格式显示它应该引起人们的关注。

在实际的金融系统中处理了这个问题之后,我可以告诉我们,我们可能想使用一个精度至少为小数点后6位(假设美元)的数字。希望因为我们在谈论货币价值,所以在这里不会让我们感到意外。有建议为C ++添加十进制类型,但是我不知道实际上有没有十进制类型。

在这里使用的最佳本机C ++类型将是long double。

其他仅使用int的方法的问题在于,我们必须存储的不仅是美分。金融交易通常会乘以非整数值,这会给我们带来麻烦,因为$ 100.25转换为10025 * 0.000123523(例如APR)会引起问题。我们最终将最终陷入浮点土地,而转换将花费我们很多钱。

现在,在大多数简单情况下都不会发生此问题。我给你一个精确的例子:

给定几千种货币值,如果我们将每个货币值乘以一个百分比然后相加,那么如果我们没有保留足够的小数位,则最终得出的数字将不同于将总数乘以该百分比后的数字。现在,这在某些情况下可能会起作用,但我们很快就会掉价几分钱。根据我的一般经验,请确保我们将精度保持在小数点后6位(确保剩下的精度可用于整数部分)。

还应了解,如果以不太精确的方式进行数学运算,则存储哪种类型都没有关系。如果数学是在单精度范围内完成的,则以双精度存储都没关系。对于最不精确的计算,精度将是正确的。

话虽如此,如果我们不做简单的加法或者减法运算,然后存储数字,那我们就可以了,但是一旦出现任何比这更复杂的事情,我们就会遇到麻烦。

如果使用基于十进制的货币,我建议使用long int以最小的面额存储货币(例如,美国货币为美分)。

非常重要:请确保根据其实际包含的货币名称来命名它们。 (示例:account_balance_cents)这将避免很多问题。

(另一个例子是百分比。当值实际上包含未乘以100的比率时,切勿将其命名为" XXX_percent"。)

查看相对较新的Intelr小数浮点数学库。它专门用于金融应用程序,并实现了一些二进制浮点算术(IEEE 754r)的新标准。

GMP库具有" bignum"实现,我们可以将其用于处理金钱所需的任意大小的整数计算。请参阅mpz_class的文档(警告:尽管提供了完整的算术运算符,但是这是非常不完整的)。

无论我们决定使用哪种类型,我都建议将其包装在" typedef"中,以便我们可以在其他时间进行更改。

一种选择是将$ 10.01存储为1001,并以便士进行所有计算,在显示值时除以100D。

或者,使用浮点数,并且仅在最后可能的时刻进行舍入。

通常,可以通过更改操作顺序来缓解问题。

使用(value * 10)/ 100代替价值* .10的10%折扣,这将有很大的帮助。 (请记住.1是重复的二进制文件)

始终将整数存储为美分(或者存储的最低货币是什么)。问题是,不管有一天用浮点数做什么,都会发现计算的结果会有所不同它在浮点数。最后一刻的四舍五入不是答案,因为实际货币计算会随其进行四舍五入。

我们也无法通过更改操作顺序来避免该问题-当百分比没有正确的二进制表示形式时,此操作将失败。如果我们只差一分钱,会计师就会感到吃惊。

我们的金融机构使用"双重"。由于我们是一家"固定收益"商店,因此,我们有很多讨厌的复杂算法,无论如何都使用double。诀窍是确保最终用户演示文稿不会超过double的精度。例如,当我们有一个总额为数万亿美元的交易清单时,我们必须确保由于舍入问题而不会打印垃圾。

不要将其存储为美分,因为在迅速乘以税金和利息时会积累错误。至少要保留两个有效数字:$ 12.45将被存储为124,500。如果将其保留为带符号的32位整数,则可以使用$ 200,000(正数或者负数)。如果我们需要更大的数字或者更高的精度,则带符号的64位整数可能会为我们提供长时间所需的所有空间。

将这个值包装在一个类中,为我们提供一个位置来创建这些值,对其进行算术并格式化它们以便显示,可能会对我们有所帮助。这也将为我们提供一个中心位置,可以随身携带所存储的货币(美元,加元,欧元等)。

了解数据范围。

浮点数仅对6到7位数的精度有好处,因此,最大数值约为+ -9999.99(无取整)。对于大多数金融应用程序来说,它是无用的。

双精度数适用于13位数字,因此:+ -99,999,999,999.99,使用大数时仍要小心。如果将两个相似的结果相减,则会损失很多精度(有关潜在问题,请参见有关数值分析的书)。

32位整数可以达到+ -2十亿(缩放到几美分将下降2个小数位)

64位整数可以处理任何费用,但是再次注意,在转换时,请乘以我们应用中的各种可能是浮点数/双精度比率的比率。

关键是要了解问题域。我们对准确性有什么法律要求?我们将如何显示这些值?转换多久进行一次?我们需要国际化吗?在做出决定之前,请确保我们可以回答这些问题。

继续写自己的钱(http://junit.sourceforge.net/doc/testinfected/testing.htm)或者currency()类(取决于需要)。并进行测试。

最大的问题是四舍五入!

42,50的19%= 8,075. 根据德国的四舍五入规则,此值为8,08. 问题是,(至少在我的机器上)8075不能表示为double。即使我将调试器中的变量更改为该值,我最终也会得到8,0749999...。

这就是我的舍入函数(以及我能想到的任何其他浮点逻辑)失败的地方,因为它产生8,07. 有效数字为4,因此该值四舍五入。这是很明显的错误,除非我们避免使用浮点值,否则我们将无能为力。

如果将42,50表示为Integer 42500000,则效果很好。

42500000 * 19/100 =8075000。现在我们可以将舍入规则应用到8080000以上。由于显示原因,可以很容易地将其转换为货币值。 8,08.

但是我总是将其包装在一个类中。