我知道使用浮点数存储货币值不合适,因为会存在舍入误差。在C++中有标准的表示货币的方法吗?
我查看了boost库但没有找到相关内容。在Java中,好像BigInteger是一种方式,但我找不到相应的C++等价物。如果有已经测试过的方案,我宁愿不自己编写货币类。
我知道使用浮点数存储货币值不合适,因为会存在舍入误差。在C++中有标准的表示货币的方法吗?
我查看了boost库但没有找到相关内容。在Java中,好像BigInteger是一种方式,但我找不到相应的C++等价物。如果有已经测试过的方案,我宁愿不自己编写货币类。
不要仅将其储存为分,因为在计算税和利息时,乘法操作会很快积累误差。最起码,保留额外的两个有效数字:$12.45应该被存储为124,500。如果您将其保存在带符号的32位整数中,您将有200,000美元可用于工作(正或负)。如果需要更大的数字或更高的精度,带符号的64位整数可能会给您足够长时间内所需的所有空间。
将该值包装在类中可能会有所帮助,这样可以为您提供一个地方来创建这些价值、对它们进行运算并为其格式化显示。这也将为您提供一个集中的地方来存储所使用的货币(USD、CAD、欧元等)。
我曾在实际的财务系统中处理过这个问题,我可以告诉你,如果是美元,你可能需要使用至少具有6位小数精度的数字。希望由于你谈论的是货币价值,你不会偏离太远。目前有关向C++添加十进制类型的提案,但我不知道是否已经实现。
在这里使用的最佳本机C++类型是long double。
其他方法只使用int的问题在于,你必须存储的不仅仅是几分钱。通常,金融交易会乘以非整数值,这将会给你带来麻烦,因为例如将$100.25翻译成10025 * 0.000123523(例如APR)就会引起问题。最终你将进入浮点数领域,并且转换将会很费力。
在大多数简单情况下,这个问题并不会发生。我给你一个明确的例子:
如果你给出几千个货币价值,如果你将每个价值乘以一个百分比然后相加,如果你不保留足够的小数位数,则最后得到的结果将与如果你将总值乘以该百分比得到的结果不同。现在这在某些情况下可能有效,但你很快就会少几分钱。根据我的一般经验,确保保留6位小数精度(确保剩余的精度可用于整数部分)。
还要明白,如果你进行不太精确的数学计算,则存储类型不重要。如果你的数学计算是以单精度运算的,那么无论你将其存储在双精度中还是其他精度中,你的精度都将正确到最不精确的计算。
话虽如此,如果你除了简单的加减法之外没有进行其他复杂的数学计算并存储数字,则不会有问题;但是只要出现任何比这更复杂的情况,你将陷入麻烦。
最大的问题在于四舍五入!
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 €。
但我总是会将其封装在一个类中。
此类型的基数是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;
}
输出:
浮点数:0.3333333432674408
cpp_dec_float: 0.3333333333333333
* 我并不是说float(基于2)不好,decimal(基于10)就是好的。它们只是表现不同...
** 我知道这是一个旧帖子,并且boost::multiprecision在2013年被引入,所以我想在这里做出备注。
cpp_dec_float_50 correct
类型将具有与double相同的精度和基础表示形式。 查看我在此处的回答:https://dev59.com/0HVC5IYBdhLWcg3w51ny#73551215,以获取更详细的解释,为什么这个回答是误导性的,并提供如何正确使用Boost多精度十进制类型的示例。 - nessus_pphttps://github.com/vpiotr/decimal_for_cpp
该数据类型旨在存储货币相关的值(余额、汇率、利率等),用户可以自定义精度,最高可达19位。我建议您保留一个变量来表示美分数量,而不是美元。这样可以避免舍入误差。将其显示为标准的美元/美分格式应该是视图层面的问题。
了解您的数据范围。
浮点数仅适用于6到7位有效数字,因此最大值约为+-9999.99,不进行四舍五入。对于大多数金融应用程序而言,它是无用的。
双精度浮点数可达到13位数字,因此:+-99,999,999,999.99,但使用大数时仍需小心。请注意,减去两个类似的结果会削弱大部分精度(有关潜在问题,请参见《数值分析》一书)。
32位整数可达到+-20亿(按分计算将降低2位小数)
64位整数可以处理任何金额,但再次请注意,在应用程序中将其转换并乘以各种比率时要小心,可能是浮点数/双精度浮点数。
关键是了解您的问题域。您需要满足哪些精度要求?您将如何显示这些值?转换将发生多少次?您需要国际化吗?在做出决策之前,请确保能够回答这些问题。
将美元和美分作为两个独立的整数存储。