67

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

我查看了 boost 库,并没有发现任何关于它的信息。在 java 中,似乎 BigInteger 是一种方式,但我在 C++ 中找不到等价物。我可以编写自己的货币类,但如果有测试,我宁愿不这样做。

4

20 回答 20

34

不要将其存储为美分,因为当您很快乘以税收和利息时,您会累积错误。至少,保留额外的两位有效数字:$12.45 将存储为 124,500。如果你把它保存在一个有符号的 32 位整数中,你将有 200,000 美元可以使用(正数或负数)。如果您需要更大的数字或更精确的数字,带符号的 64 位整数可能会为您提供长时间所需的所有空间。

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

于 2008-09-29T16:01:08.460 回答
28

在实际的金融系统中处理过这个问题后,我可以告诉你,你可能想要使用一个至少有 6 个小数位精度的数字(假设是美元)。希望既然你在谈论货币价值,你就不会在这里失控。有向 C++ 添加十进制类型的建议,但我不知道实际存在的任何类型。

此处使用的最佳本机 C++ 类型是 long double。

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

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

给定几千个货币值,如果您将每个值乘以一个百分比然后将它们相加,那么您最终将得到一个不同的数字,而不是如果您没有保留足够的小数位,您将总数乘以该百分比。现在这可能在某些情况下有效,但您通常会很快减少几美分。根据我的一般经验,确保您保持最多 6 位小数的精度(确保剩余的精度可用于整数部分)。

还要了解,如果您以不太精确的方式进行数学运算,则存储它的类型并不重要。如果您的数学是在单精度土地上完成的,那么您是否以双精度存储它并不重要。您的精度对于最不精确的计算是正确的。


话虽如此,如果你只做简单的加法或减法运算,然后存储数字,那么你会没事的,但是一旦出现比这更复杂的东西,你就会有麻烦了。

于 2008-09-29T14:59:31.993 回答
24

查看相对较新的Intelr Decimal Floating-Point Math Library。它专门用于金融应用程序,并实现了一些二进制浮点运算的新标准 (IEEE 754r)

于 2008-09-29T15:09:35.150 回答
13

最大的问题是四舍五入!

42,50 欧元的 19% = 8,075 欧元。由于德国的四舍五入规则,这是 8,08 欧元。问题是,(至少在我的机器上)8,075 不能表示为双精度。即使我将调试器中的变量更改为此值,我最终还是 8,0749999....

这就是我的舍入函数(以及我能想到的任何其他浮点逻辑)失败的地方,因为它产生 8,07 欧元。有效数字为 4,因此该值向下舍入。这是完全错误的,除非您尽可能避免使用浮点值,否则您将无能为力。

如果您将 42,50 € 表示为整数 42500000,则效果很好。

42500000 * 19 / 100 = 8075000。现在您可以应用 8080000 以上的舍入规则。出于显示原因,这可以很容易地转换为货币值。8,08 欧元。

但我总是会在课堂上把它包起来。

于 2010-07-08T15:07:00.247 回答
9

我建议您为美分而不是美元的数量保留一个变量。那应该消除舍入误差。以标准美元/美分格式显示它应该是一个视图问题。

于 2008-09-29T14:55:48.867 回答
9

您可以尝试十进制数据类型:

https://github.com/vpiotr/decimal_for_cpp

旨在存储以货币为导向的值(货币余额、货币汇率、利率),用户定义的精度。最多 19 位数字。

它是 C++ 的仅标头解决方案。

于 2011-01-04T00:37:43.837 回答
9

你说你已经查看了 boost 库,但没有发现任何关于那里的信息。但是你有multiprecision/cpp_dec_float说:

这种类型的基数是 10。因此,它的行为可能与 base-2 类型略有不同。

因此,如果您已经在使用 Boost,这应该对货币价值和操作有好处,因为它的基数为 10 数字和 50 或 100 位精度(很多)。

看:

#include <iostream>
#include <iomanip>
#include <boost/multiprecision/cpp_dec_float.hpp>

int main()
{
    float bogus = 1.0 / 3.0;
    boost::multiprecision::cpp_dec_float_50 correct = 1.0 / 3.0;

    std::cout << std::setprecision(16) << std::fixed 
              << "float: " << bogus << std::endl
              << "cpp_dec_float: " << correct << std::endl;
          
    return 0;
}

输出:

浮动:0.3333333432674408

cpp_dec_float: 0.3333333333333333

*我并不是说浮点数(以 2 为底)不好,而十进制(以 10 为底)是好的。他们只是表现不同...

** 我知道这是一篇旧帖子,并且 boost::multiprecision 是在 2013 年推出的,所以想在这里评论一下。

于 2017-08-28T01:29:24.973 回答
7

了解您的数据范围。

浮点数仅适用于 6 到 7 位的精度,因此这意味着最大值约为 +-9999.99 而没有四舍五入。对于大多数金融应用程序来说,它是无用的。

双精度数适用于 13 位数字,因此:+-99,999,999,999.99,使用大数字时仍要小心。认识到减去两个相似的结果会剥夺大部分精度(有关潜在问题,请参阅有关数值分析的书)。

32 位整数适合 +-20 亿(缩放到便士将减少 2 个小数位)

64 位整数可以处理任何钱,但同样,在转换时要小心,并乘以您的应用程序中可能是浮动/双倍的各种费率。

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

于 2008-09-29T16:18:37.043 回答
5

无论您决定选择哪种类型,我都建议您将其包装在“typedef”中,以便您可以在不同时间更改它。

于 2008-09-29T15:14:36.323 回答
4

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

于 2008-09-29T14:55:38.350 回答
2

整数,总是——将它存储为美分(或任何你正在编程的最低货币。)问题是,无论你用浮点数做什么,总有一天你会发现如果你这样做,计算会有所不同它是浮点数。最后一分钟的四舍五入不是答案,因为实际货币计算在进行时会四舍五入。

你也不能通过改变操作的顺序来避免这个问题——当你有一个百分比让你没有正确的二进制表示时,这会失败。如果你差一分钱,会计师会发疯的。

于 2008-09-29T15:26:27.637 回答
2

将美元和美分金额存储为两个单独的整数。

于 2016-03-15T18:07:58.963 回答
1

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

非常重要:确保根据实际包含的内容命名所有货币值。(例如:account_balance_cents)这将避免很多问题。

(另一个出现这种情况的例子是百分比。当它实际上包含一个不乘以一百的比率时,切勿将值命名为“XXX_percent”。)

于 2008-09-29T15:03:12.357 回答
1

解决方案很简单,将所需的精度存储为移位整数。但是当读入转换为双浮点数时,计算会遭受更少的舍入错误。然后在数据库中存储时乘以所需的任何整数精度,但在截断为整数之前添加 +/- 1/10 以补偿截断错误,或 +/- 51/100 进行舍入。十分简单。

于 2014-10-15T01:48:24.647 回答
0

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

于 2008-09-29T15:11:01.107 回答
0

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

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

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

不要使用 value * .10 来获得 10% 的折扣,而是使用 (value * 10)/100,这将有很大帮助。(记住 .1 是一个重复的二进制文件)

于 2008-09-29T15:19:24.130 回答
0

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

于 2008-09-29T15:50:56.457 回答
0

对于 32 位,我会使用有符号长,对于 64 位,我会使用有符号长。这将为您提供基础数量本身的最大存储容量。然后我会开发两个自定义操纵器。一种是根据汇率转换该数量,另一种是将该数量格式化为您选择的货币。您可以为各种金融操作/规则开发更多的操纵器。

于 2016-07-22T17:07:47.393 回答
0

这是一个非常古老的帖子,但我想我会更新一点,因为已经有一段时间了,事情已经发生了变化。我在下面发布了一些代码,这些代码代表了我能够使用编程语言long long integer中的数据类型来表示货币的最佳方式。C

#include <stdio.h>

int main()
{
    // make BIG money from cents and dollars
    signed long long int cents = 0;
    signed long long int dollars = 0;

    // get the amount of cents
    printf("Enter the amount of cents: ");
    scanf("%lld", &cents);

    // get the amount of dollars
    printf("Enter the amount of dollars: ");
    scanf("%lld", &dollars);

    // calculate the amount of dollars
    long long int totalDollars = dollars + (cents / 100);

    // calculate the amount of cents
    long long int totalCents = cents % 100;

    // print the amount of dollars and cents
    printf("The total amount is: %lld dollars and %lld cents\n", totalDollars, totalCents);
}
于 2021-09-12T21:15:06.637 回答
-2

继续写你自己的钱(http://junit.sourceforge.net/doc/testinfected/testing.htm)或货币()类(取决于你需要什么)。并测试它。

于 2008-09-29T18:35:35.750 回答