问题
We have a problem with rounding of floating point numbers for some financial calculations.
Basically, we want to round monetary amounts like 1000000000.555 to 2 decimals. However, the float representation of this number is 1000000000.5549999 and as a result we will round down to 1000000000.55 rather than the correct 1000000000.56.
Is there any way we can come around this problem in a safe way? The only thing we have come up with so far is to always add the smallest representable float to the amount before the rounding operation, but we don't know if this is safe in all cases.
The code is written in C and need to run on windows32/64/linux/solaris so we unfortunately do not have access to nice stuff like the Decimal datatype in .net.
Any input would be helpful.
Thanks, Rickard
回答1:
The proper way to represent currencies most often is using integers. For instance, in the Euro zone a common approach is to represent values in micro-euro's (1E-6). You'd obviously use 64 bits math for this. You'd consistently use this throughout the application. Only on human I/O would you round, and you would do so by dividing by 10000 to get an integer amount in cents.
回答2:
The moral of this tale is never, ever use floating point numbers to represent money!
Money comes in discrete amounts, and, most financial and accounting regulations acknowledge this. You cannot physically pay a bill for $3.145217 dollars. While in the 17th century it was acceptable to take a coin to a blacksmith and get him to cut a silver dollar into pieces (Pieces of Eight had nice pizza slice markings to aid this process!)today its just not possible.
For instance the smallest coin available in Switzerland is 5 rappe, so accounting and bills must be expressed to the nearest 5 cents i.e you cannot get a bill for CHF 3.14 it must be CHF 3.15, or CHF 3.10 in the unlikely event that you supplier is generous, because you could can only pay either 3.10 or 3.15 in cash.
There are two options open too you. Get the boost BigDecimal library, which would allow to specify you roundings precisely. Or as another poster suggested use long long representing your amounts in thousandths of Euros and only rounding on display.
A further possibility is to use units of half-cents i.e. 55 Euros 35 cents is represented internally as 11070 half cents then should not need to worry about rounding for any standard accounting transaction.
I cant help worrying that you have not captured your business requirements properly. On my last project there were a over a hundred pages of business rules dealing with interest rate calculations at least 40 pages of this concerned the number of decimal places at each stage of a calculation or the rounding algorithim to be used.
回答3:
I recommend to use a decimal number package, such as decNumber.
回答4:
If you are in windows and are willing to write managed C/C++.
In most cases, you can just use the decimal class. From MSDN
The Decimal value type is appropriate for financial calculations requiring large numbers of significant integral and fractional digits and no round-off errors.
If you are not able or willing to use managed C/C++, look at decimal number package, such as decNumber.
In both cases you should have a lot of unit and integration tests that cover the corner cases with rounding. They will give you the most pain. Also rounding is very good at hiding other bugs.
However as other have said be very careful where you round. E.g In the UK income tax software spec about half the text is about where and how (different in each place) to round numbers, as you have to get the same results then using manual tax table.
来源:https://stackoverflow.com/questions/1326659/rounding-floats-with-non-exact-representation