在 C++ 中存储货币值的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/149033/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to store currency values in C++
提问by
I know that a float isn't appropriate to store currency values because of rounding errors. Is there a standard way to represent money in C++?
我知道由于四舍五入错误,浮点数不适合存储货币价值。在 C++ 中是否有表示金钱的标准方法?
I've looked in the boost library and found nothing about it. In java, it seems that BigInteger is the way but I couldn't find an equivalent in C++. I could write my own money class, but prefer not to do so if there is something tested.
我查看了 boost 库,但一无所获。在java中,BigInteger似乎是一种方式,但我在C++中找不到等价物。我可以编写自己的货币类,但如果有一些经过测试的东西,我宁愿不这样做。
回答by Eclipse
Don't store it just as cents, since you'll accumulate errors when multiplying for taxes and interest pretty quickly. At the very least, keep an extra two significant digits: $12.45 would be stored as 124,500. If you keep it in a signed 32 bit integer, you'll have $200,000 to work with (positive or negative). If you need bigger numbers or more precision, a signed 64 bit integer will likely give you all the space you'll need for a long time.
不要将其存储为美分,因为在快速乘以税收和利息时,您会累积错误。至少,保留一个额外的两位有效数字:$12.45 将存储为 124,500。如果您将它保存在一个有符号的 32 位整数中,您将有 200,000 美元可以使用(正数或负数)。如果您需要更大的数字或更高的精度,一个有符号的 64 位整数可能会为您提供长时间所需的所有空间。
It might be of some help to wrap this value in a class, to give you one place for creating these values, doing arithmetic on them, and formatting them for display. This would also give you a central place to carry around which currency it being stored (USD, CAD, EURO, etc).
将此值包装在一个类中可能会有所帮助,以便为您提供一个位置来创建这些值、对它们进行算术运算以及格式化它们以进行显示。这也将为您提供一个中央位置来携带所存储的货币(美元、加元、欧元等)。
回答by Orion Adrian
Having dealt with this in actual financial systems, I can tell you you probably want to use a number with at least 6 decimal places of precision (assuming USD). Hopefully since you're talking about currency values you won't go way out of whack here. There are proposals for adding decimal types to C++, but I don't know of any that are actually out there yet.
在实际的金融系统中处理过这个问题后,我可以告诉你,你可能想要使用一个至少有 6 个小数位精度的数字(假设是美元)。希望既然你在谈论货币价值,你就不会在这里失控。有将十进制类型添加到 C++ 的建议,但我不知道实际上有任何建议。
The best native C++ type to use here would be long double.
此处使用的最佳本机 C++ 类型是 long double。
The problem with other approaches that simply use an int is that you have to store more than just your cents. Often financial transactions are multiplied by non-integer values and that's going to get you in trouble since $100.25 translated to 10025 * 0.000123523 (e.g. APR) is going cause problems. You're going to eventually end up in floating point land and the conversions are going to cost you a lot.
其他仅使用 int 的方法的问题在于,您必须存储的不仅仅是美分。通常金融交易乘以非整数值,这会给您带来麻烦,因为 100.25 美元转换为 10025 * 0.000123523(例如 APR)会导致问题。您最终将进入浮点领域,转换将花费您很多钱。
Now the problem doesn't happen in most simple situations. I'll give you a precise example:
现在这个问题在大多数简单的情况下都不会发生。我给你一个准确的例子:
Given several thousand currency values, if you multiply each by a percentage and then add them up, you will end up with a different number than if you had multiplied the total by that percentage if you do not keep enough decimal places. Now this might work in some situations, but you'll often be several pennies off pretty quickly. In my general experience making sure you keep a precision of up to 6 decimal places (making sure that the remaining precision is available for the whole number part).
给定几千个货币价值,如果您将每个货币值乘以一个百分比,然后将它们相加,则最终得到的数字与将总数乘以该百分比的数字不同,如果您没有保留足够的小数位。现在这在某些情况下可能会奏效,但您通常很快就会减少几分钱。根据我的一般经验,确保您保持最多 6 位小数的精度(确保剩余精度可用于整数部分)。
Also understand that it doesn't matter what type you store it with if you do math in a less precise fashion. If your math is being done in single precision land, then it doesn't matter if you're storing it in double precision. Your precision will be correct to the least precise calculation.
还要了解,如果您以不太精确的方式进行数学运算,则存储它的类型无关紧要。如果您的数学是在单精度范围内完成的,那么您是否以双精度存储它并不重要。您的精度对于最不精确的计算是正确的。
Now that said, if you do no math other than simple addition or subtraction and then store the number then you'll be fine, but as soon as anything more complex than that shows up, you're going to be in trouble.
话虽如此,如果您除了简单的加法或减法之外不进行数学运算,然后存储数字,那么您会没事的,但是一旦出现比这更复杂的东西,您就会遇到麻烦。
回答by jblocksom
Look in to the relatively recent Intelr Decimal Floating-Point Math Library. It's specifically for finance applications and implements some of the new standards for binary floating point arithmetic (IEEE 754r).
查看相对较新的Intelr Decimal Floating-Point Math Library。它专门用于金融应用程序,并实现了一些二进制浮点算法 (IEEE 754r)的新标准。
回答by user331471
The biggest issue is rounding itself!
最大的问题是四舍五入!
19% of 42,50 = 8,075 . Due to the German rules for rounding this is 8,08 . The problem is, that (at least on my machine) 8,075 can't be represented as double. Even if I change the variable in the debugger to this value, I end up with 8,0749999....
19% 的 42,50 = 8,075 。由于四舍五入的德国规则,这是 8,08 。问题是,(至少在我的机器上)8,075 不能表示为双精度。即使我将调试器中的变量更改为这个值,我最终也会得到 8,0749999....
And this is where my rounding function (and any other on floating point logic that I can think of) fails, since it produces 8,07 . The significant digit is 4 and so the value is rounded down. And that is plain wrong and you can't do anything about it unless you avoid using floating point values wherever possible.
这就是我的舍入函数(以及我能想到的任何其他浮点逻辑)失败的地方,因为它产生 8,07 。有效数字为 4,因此该值向下舍入。这是完全错误的,除非您尽可能避免使用浮点值,否则您对此无能为力。
It works great if you represent 42,50 as Integer 42500000.
如果您将 42,50 表示为整数 42500000,则效果很好。
42500000 * 19 / 100 = 8075000. Now you can apply the rounding rule above 8080000. This can easily be transformed to a currency value for display reasons. 8,08 .
42500000 * 19 / 100 = 8075000。现在您可以应用高于 8080000 的舍入规则。出于显示原因,这可以轻松转换为货币值。8,08 。
But I would always wrap that up in a class.
但我总是把它放在一个班级里。
回答by Rontologist
I would suggest that you keep a variable for the number of cents instead of dollars. That should remove the rounding errors. Displaying it in the standards dollars/cents format should be a view concern.
我建议你保留一个变量来表示美分而不是美元。这应该消除舍入误差。以标准美元/美分格式显示它应该是一个视图问题。
回答by Piotr
You can try decimal data type:
您可以尝试十进制数据类型:
https://github.com/vpiotr/decimal_for_cpp
https://github.com/vpiotr/decimal_for_cpp
Designed to store money-oriented values (money balance, currency rate, interest rate), user-defined precision. Up to 19 digits.
旨在存储面向货币的值(货币余额、货币利率、利率),用户定义的精度。最多 19 位数字。
It's header-only solution for C++.
它是 C++ 的头文件解决方案。
回答by Fernando
You say you've looked in the boost library and found nothing about there. But there you have multiprecision/cpp_dec_floatwhich says:
你说你已经查看了 boost 库,但一无所获。但是你有multiprecision/cpp_dec_float它说:
The radix of this type is 10. As a result it can behave subtly differently from base-2 types.
此类型的基数为 10。因此,它的行为与基数 2 类型略有不同。
So if you're already using Boost, this should be good to currency values and operations, as its base 10 number and 50 or 100 digits precision (a lot).
因此,如果您已经在使用 Boost,这对货币价值和操作应该是有益的,因为它的基数为 10 数字和 50 或 100 位精度(很多)。
See:
看:
#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;
}
Output:
输出:
float: 0.3333333432674408
cpp_dec_float: 0.3333333333333333
浮动:0.3333333432674408
cpp_dec_float:0.33333333333333333
*I'm not saying float (base 2) is bad and decimal (base 10) is good. They just behave differently...
*我不是说浮点数(基数 2)不好,小数(基数 10)是好的。他们只是表现不同……
** I know this is an old post and boost::multiprecision was introduced in 2013, so wanted to remark it here.
** 我知道这是一篇旧帖子,boost::multiprecision 是在 2013 年推出的,所以想在这里评论一下。
回答by Dan Hewett
Know YOUR range of data.
了解您的数据范围。
A float is only good for 6 to 7 digits of precision, so that means a max of about +-9999.99 without rounding. It is useless for most financial applications.
浮点数仅适用于 6 到 7 位精度,因此这意味着最大约为 +-9999.99,无需四舍五入。对于大多数金融应用程序来说,它是无用的。
A double is good for 13 digits, thus: +-99,999,999,999.99, Still be careful when using large numbers. Recognize the subtracting two similar results strips away much of the precision (See a book on Numerical Analysis for potential problems).
double 适用于 13 位数字,因此:+-99,999,999,999.99,使用大数时仍要小心。认识到将两个相似的结果相减会降低很多精度(有关潜在问题,请参阅有关数值分析的书)。
32 bit integer is good to +-2Billion (scaling to pennies will drop 2 decimal places)
32 位整数可以达到 +-20 亿(缩放到便士将减少 2 位小数)
64 bit integer will handle any money, but again, be careful when converting, and multiplying by various rates in your app that might be floats/doubles.
64 位整数可以处理任何钱,但同样,在转换时要小心,并乘以应用程序中可能是浮点数/双倍数的各种费率。
The key is to understand your problem domain. What legal requirements do you have for accuracy? How will you display the values? How often will conversion take place? Do you need internationalization? Make sure you can answer these questions before you make your decision.
关键是要了解您的问题域。您对准确性有哪些法律要求?你将如何显示这些值?转换多久发生一次?你需要国际化吗?在做出决定之前,请确保您可以回答这些问题。
回答by Jordan Parmer
Whatever type you do decide on, I would recommend wrapping it up in a "typedef" so you can change it at a different time.
无论您决定选择哪种类型,我都建议将其包装在“typedef”中,以便您可以在不同的时间进行更改。
回答by Douglas Mayle
It depends on your business requirements with regards to rounding. The safest way is to store an integer with the required precision and know when/how to apply rounding.
这取决于您对舍入的业务要求。最安全的方法是存储具有所需精度的整数并知道何时/如何应用舍入。