用C ++存储货币值的最佳方法
我知道浮动不适合存储货币值,因为四舍五入错误。 有没有一种标准的方式来代表C ++的钱?
我已经看到了增强库,并没有发现任何关于它。 在java中,似乎BigInteger是这样的,但我无法在C ++中find等价物。 我可以写自己的钱课,但是如果有什么testing的话,宁愿不要这么做。
不要把它存储成美分,因为当税收和利息快速增长时,你会积累错误。 至less保留两位有效数字:$ 12.45将被存储为124,500。 如果你保存在一个有符号的32位整数,你将有20万美元的工作(积极或消极)。 如果你需要更大的数字或更高的精度,一个有符号的64位整数可能会给你所有的空间,你需要很长一段时间。
在一个类中包装这个值可能会有一些帮助,给你一个创build这些值的地方,对它们进行算术运算,然后格式化它们以供显示。 这也可以给你一个中央位置来存放货币(USD,CAD,EURO等)。
查看最近的Intelr十进制浮点math库 。 它专门用于财务应用程序,并实现了二进制浮点运算(IEEE 754r)的一些新标准 。
在实际的金融系统中处理了这个问题之后,我可以告诉你,你可能想要使用一个至less有6个小数位的数字(假设是USD)。 希望既然你在谈论货币价值,那么你不会在这里摆脱困境。 有build议为C ++添加十进制types,但我不知道任何实际上在那里。
在这里使用的最好的本地C ++types将是双重的。
其他简单使用int的方法存在的问题是,您必须存储更多的分数。 通常金融交易乘以非整数值,这将让你陷入困境,因为100.25美元翻译成10025 * 0.000123523(如APR)会造成问题。 你将最终在浮点土地,转换将花费你很多。
现在这个问题在最简单的情况下不会发生。 我会给你一个确切的例子:
给定几千个货币值,如果你乘以一个百分比,然后把它们相加,那么如果你没有保留足够的小数位数,那么最终的结果将会是一个不同的数字。 现在这在某些情况下可能会起作用,但是通常会很快closures几个便士。 在我的一般经验,确保你保持精度高达6位小数(确保其余的精度是可用的整个数字部分)。
也明白,如果你用不太精确的方式进行math运算,那么存储它的types并不重要。 如果你的math是在单精度土地上完成的,那么你是否以双精度存储它并不重要。 您的精度将是最准确的计算是正确的。
既然如此,如果除了简单的加法或减法之外没有其他的math方法,然后存储这个数字,那么你会没事的,但是一旦出现比这更复杂的事情,你就会陷入困境。
最大的问题是四舍五入!
42,50€的19%= 8075€。 由于德国的四舍五入法则是8,08欧元。 问题是,(至less在我的机器上)8,075不能表示为double。 即使我将debugging器中的variables更改为这个值,我最终得到了8,074,999 ….
这是我的四舍五入function(以及任何其他浮点逻辑,我能想到)失败,因为它产生8,07欧元。 有效数字是4,所以数值向下舍入。 这是明显的错误,你不能做任何事情,除非你尽可能避免使用浮点值。
如果以42,50欧元来代表整数42500000,那么它就很好用。
42500000 * 19/100 = 8075000.现在,您可以将舍入规则应用于8080000以上。由于显示原因,可以很容易地将其转换为货币值。 8,08€。
但是我会一直把这个包装起来。
我build议你保留一个变数,而不是美元数。 这应该删除舍入错误。 以标准美元/美分格式显示它应该是一个关注的问题。
无论你决定什么types,我都会build议将它包装在“typedef”中,以便在不同的时间更改它。
了解你的数据范围。
浮点数只能用于精度为6到7位的数字,所以这意味着最大值大约为+ -9999.99,而不是四舍五入。 对于大多数财务应用程序是没有用的。
一个双精度13位数字,因此:+ -99,999,999,999.99,使用大数字时还是要小心。 认识到减去两个类似的结果带走了很多的精度(见数字分析潜在问题的书)。
32位整数是好的+ -2亿(缩放到便士会下降2位小数)
64位整数将处理任何金钱,但再次,转换时要小心,并在您的应用程序中乘以各种费率,可能是浮动/双打。
关键是了解你的问题领域。 你对准确性有什么法定要求? 你将如何显示值? 转换的频率如何? 你需要国际化吗? 在做出决定之前,确保你能回答这些问题。
你可以尝试十进制数据types:
https://github.com/vpiotr/decimal_for_cpp
旨在存储以货币为导向的价值(货币余额,货币汇率,利率),用户定义的精确度。 最多19位数字。
这是C ++的头文件解决scheme。
这取决于您的业务需求四舍五入。 最安全的方法是存储一个具有所需精度的整数,并知道何时/如何应用舍入。
整数,总是 – 存储为美分(或任何你的最低货币是你编程的地方)。问题是,无论你做什么浮点有一天,你会发现一个情况,计算将有所不同,如果你它在浮点。 在最后一刻舍入并不是答案,因为实际货币计算是四舍五入的。
你也不能通过改变操作的顺序来避免这个问题 – 当你有一个百分比没有适当的二进制表示时,这个失败。 如果你只有一分钱,会计师就会怪怪的。
如果使用基于十进制的货币,我会build议使用长整数来存储最小面额的货币(例如,美国货币将是美分)。
非常重要:请务必根据它们实际包含的内容命名所有货币值。 (例如:account_balance_cents)这将避免很多问题。
(另一个例子是百分比,当它实际上包含一个不乘以百分之一的比率时,永远不要命名一个值“XXX_percent”。)
GMP库具有“bignum”实现,您可以将其用于处理金钱所需的任意大小的整数计算。 请参阅mpz_class的文档(警告:虽然提供了全系列的算术运算符,但是这是非常不完整的) 。
一个选项是将$ 10.01存储为1001,并在显示值时以便士为单位进行所有计算,除以100D。
或者,使用浮游物,并且只在最后可能的时刻进行。
通常情况下,问题可以通过改变操作顺序来缓解。
10%折扣的价值* .10而不是价值* .10,使用(价值* 10)/ 100,这将有助于显着。 (记住.1是一个重复的二进制)
我们的金融机构使用“双”。 由于我们是一个“固定收入”商店,所以我们有许多令人讨厌的复杂algorithm,无论如何都使用双重algorithm。 诀窍是确保您的最终用户演示文稿不会超出double的精度。 例如,当我们有一个总额为数万亿美元的交易清单时,由于四舍五入问题,我们确信我们不会印刷垃圾。
继续写你自己的钱( http://junit.sourceforge.net/doc/testinfected/testing.htm )或货币()类(取决于你需要什么)。 并testing它。
该解决scheme很简单,存储到需要的任何精度,作为一个移位的整数。 但是在读取时转换为双精度浮点数,这样计算就会有更less的舍入误差。 然后当存储在数据库中时,需要乘以任何整数精度,但是在截取为整数之前加上+/- 1/10来补偿截断误差,或者+/- 51/100来进行舍入。 十分简单。
将美元和美分存储为两个独立的整数。
我使用32位长签名,长64位签名。 这将为您提供底层数量本身的最大存储容量。 然后我会开发两个自定义操纵器。 一种是根据汇率转换数量,另一种是将数量转换成您select的货币。 你可以开发更多的操纵者的各种金融业务和规则。
你说你已经看了助推库,并没有发现任何关于那里。 但是你有multiprecision / cpp_dec_float说:
这种types的基数是10.因此,它可以与基本types2有微妙的不同。
因此,如果您已经使用了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
*我不是说float(base 2)是坏的,decimal(base 10)是好的。 他们的行为有所不同
**我知道这是一个旧的post,boost :: multiprecision是在2013年推出的,所以想在这里注明。