我知道由于舍入错误,浮点数不适合存储货币值。有没有一种标准的方式来代表C ++中的钱?
我查看过boost库并没有发现它。在java中,似乎BigInteger就是这样,但我找不到C ++中的等价物。我可以写自己的金钱课程,但如果有测试的话,我宁愿不这样做。
答案 0 :(得分:30)
不要将它存储为美分,因为当税收和利息快速增加时,你会积累错误。至少,保留额外的两位有效数字:12.45美元将存储为124,500。如果你将它保存在一个带符号的32位整数中,你将有200,000美元可以使用(正面或负面)。如果您需要更大的数字或更高的精度,签名的64位整数可能会为您提供长时间所需的所有空间。
将这个值包装在一个类中可能会有所帮助,为您提供一个位置来创建这些值,对它们进行算术运算以及格式化它们以供显示。这也可以为您提供一个携带存储货币的中心位置(美元,加元,欧元等)。
答案 1 :(得分:21)
在实际的金融系统中处理过这个问题,我可以告诉你,你可能想要使用一个至少有6位小数位数的数字(假设为美元)。希望既然你在谈论货币价值,你就不会在这里摆脱困境。有关于向C ++添加十进制类型的建议,但我不知道其中有任何实际存在。
这里使用的最佳本机C ++类型将是长双倍。
使用int的其他方法的问题是你必须存储的不仅仅是你的分数。通常情况下,金融交易会乘以非整数值,这会让您陷入困境,因为100.25美元转换为10025 * 0.000123523(例如APR)会导致问题。你最终将最终进入浮点土地并且转换将花费你很多。
现在问题在大多数简单情况下都不会发生。我会给你一个确切的例子:
给出几千个货币值,如果您将每个货币值乘以一个百分比然后将它们相加,那么如果您没有保留足够的小数位,则最终会得到一个不同的数字。现在这可能在某些情况下起作用,但是你很快就会有几便士。根据我的一般经验,确保您保持最多6位小数的精度(确保剩余的精度可用于整个数字部分)。
同样明白,如果以不太精确的方式进行数学运算,那么存储它的类型并不重要。如果你的数学是在单精度的土地上完成的,那么你是否以双精度存储它并不重要。您的精确度对于最不精确的计算是正确的。
现在说,如果除了简单的加法或减法之外你没有数学然后存储数字那么你就没事了,但只要出现比这更复杂的东西,你就会遇到麻烦
答案 2 :(得分:20)
查看相对较新的Intelr Decimal Floating-Point Math Library。它专门用于财务应用程序,并实现了一些new standards for binary floating point arithmetic (IEEE 754r)。
答案 3 :(得分:10)
最大的问题是四舍五入!
42,50€中的19%= 8,075€。由于德国的四舍五入规则,这是8,08欧元。问题是,(至少在我的机器上)8,075不能表示为double。即使我将调试器中的变量更改为此值,我最终也会得到8,0749999 ....
这是我的舍入函数(以及我能想到的任何其他浮点逻辑)失败的地方,因为它产生8,07€。有效数字为4,因此值向下舍入。这是完全错误的,除非你尽可能避免使用浮点值,否则你无法做任何事情。
如果你代表42,50€作为整数42500000,它会很有用。
42500000 * 19/100 = 8075000.现在您可以应用8080000以上的舍入规则。出于显示原因,可以很容易地将其转换为货币值。 8,08€。
但我会在课堂上把它包起来。
答案 4 :(得分:8)
我建议您保留一个变量,而不是美元。这应该删除舍入错误。以标准美元/美分格式显示它应该是一个视角问题。
答案 5 :(得分:5)
您可以尝试十进制数据类型:
https://github.com/vpiotr/decimal_for_cpp
旨在存储以货币为导向的价值(货币余额,货币汇率,利率),用户定义的精确度。最多19位数。
它是C ++的仅限标题的解决方案。
答案 6 :(得分:5)
无论你决定使用哪种类型,我都建议将其包装在“typedef”中,这样你就可以在不同的时间进行更改。
答案 7 :(得分:5)
了解您的数据范围。
浮点数仅适用于6到7位数的精度,因此这意味着最大值约为+ -9999.99而不进行舍入。对大多数金融应用来说都没用。
双倍适用于13位数,因此:+ -99,999,999,999.99,使用大数字时仍要小心。认识到减去两个相似的结果带走了大部分精度(参见有关潜在问题的数值分析书)。
32位整数好到+ -2亿(缩放到便士将下降2位小数)
64位整数将处理任何金钱,但再次,转换时要小心,并乘以您的应用程序中可能是浮动/双倍的各种费率。
关键是要了解您的问题域。您对准确性有哪些法律要求?你将如何显示这些值?转换的频率如何?你需要国际化吗?在做出决定之前,请确保您可以回答这些问题。
答案 8 :(得分:4)
这取决于您对四舍五入的业务要求。最安全的方法是存储一个具有所需精度的整数,并知道何时/如何应用舍入。
答案 9 :(得分:3)
你说你已经查看了升级库并且没有发现任何相关内容。 但是你有multiprecision/cpp_dec_float说:
此类型的基数为10.因此,它可以与基数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;
}
输出:
float:0.3333333432674408
cpp_dec_float:0.3333333333333333
*我不是说浮动(基数2)是坏的,小数(基数10)是好的。他们只是表现得不一样......
**我知道这是一篇很老的帖子,2013年推出了boost :: multiprecision,所以想在这里说一遍。
答案 10 :(得分:2)
整数,总是 - 将它存储为美分(或者你编程的最低货币。)问题是,无论你使用浮点数做什么,总有一天你会发现计算会有所不同的情况如果你以浮点方式做到这一点。最后一分钟的舍入不是答案,因为实际货币计算会随着它们四舍五入。
您无法通过更改操作顺序来避免此问题 - 如果您的百分比没有正确的二进制表示,则会失败。如果你只差一分钱,会计师就会发疯。
答案 11 :(得分:1)
解决方案很简单,存储到所需的精度,作为移位整数。但是当读入转换为双浮点数时,计算会减少舍入误差。然后当存储在数据库中时,乘以所需的整数精度,但在截断为整数之前加上+/- 1/10以补偿截断误差,或+/- 51/100来舍入。 容易腻。
答案 12 :(得分:1)
如果使用基于小数的货币,我建议使用long int来存储最小面额的货币(例如,美国货币将是美分)。
非常重要:请务必根据实际包含的内容命名所有货币值。 (例如:account_balance_cents)这将避免很多问题。
(另一个出现这种情况的例子是百分比。当实际包含的比率不是乘以100时,永远不要将值命名为“XXX_percent”。)
答案 13 :(得分:0)
我们的金融机构使用“双”。由于我们是一家“固定收益”商店,我们有许多令人讨厌的复杂算法,无论如何都使用双倍。诀窍是确保您的最终用户演示文稿不会超出双精度。例如,当我们有一个总计数万亿美元的交易清单时,我们必须确保我们不会因为四舍五入问题而打印垃圾。
答案 14 :(得分:0)
继续写你自己的钱(http://junit.sourceforge.net/doc/testinfected/testing.htm)或货币()类(取决于你需要的)。并测试它。
答案 15 :(得分:0)
一个选项是将$ 10.01存储为1001,并以便士的形式进行所有计算,并在显示值时除以100D。
或者,使用花车,并且只在最后一刻圆。
通常可以通过改变操作顺序来缓解问题。
使用(值* 10)/ 100代替价值* .10以获得10%的折扣,这将有助于显着提升。 (记住.1是一个重复的二进制文件)
答案 16 :(得分:0)
答案 17 :(得分:0)
将美元和分金额存储为两个单独的整数。
答案 18 :(得分:0)
我使用signed long for 32-bit和signed long long for 64-bit。这将为您提供基础数量本身的最大存储容量。然后我会开发两个自定义操纵器。根据汇率转换该数量的数据,以及将该数量格式化为您选择的货币的数量。您可以为各种财务操作/规则开发更多操纵器。