简体   繁体   English

圆形浮动具有非精确表示

[英]Rounding floats with non-exact representation

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. 基本上,我们希望将货币金额(如1000000000.555)舍入为2位小数。 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. 但是,此数字的浮点表示为1000000000.5549999,因此我们将向下舍入到1000000000.55而不是正确的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. 代码是用C语言编写的,需要在windows32 / 64 / linux / solaris上运行,所以我们很遗憾无法获得像.net中的Decimal数据类型这样的好东西。

Any input would be helpful. 任何输入都会有所帮助。

Thanks, Rickard 谢谢,里卡德

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). 例如,在欧元区,一种常见的方法是用微欧元(1E-6)来表示价值。 You'd obviously use 64 bits math for this. 你显然会使用64位数学。 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. 只有在人类I / O上你才能完成,你可以通过除以10000得到一个整数的分数。

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. 你不能以3.145217美元的身价支付账单。 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. 虽然在17世纪,将硬币投入铁匠并让他将一块银币切成碎片是可以接受的(八块碎片有很好的披萨切片标记来帮助这个过程!)今天它是不可能的。

For instance the smallest coin available in Switzerland is 5 rappe, so accounting and bills must be expressed to the nearest 5 cents ie 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. 例如,瑞士最小的硬币是5 rappe,因此会计和账单必须表示为最接近的5美分,即您无法获得3.14瑞士法郎的账单,必须是3.15瑞士法郎,或者如果您的供应商不太可能,则为3.10瑞士法郎慷慨,因为你只能支付3.10或3.15现金。

There are two options open too you. 你也有两种选择。 Get the boost BigDecimal library, which would allow to specify you roundings precisely. 获取boost BigDecimal库,它可以精确指定舍入。 Or as another poster suggested use long long representing your amounts in thousandths of Euros and only rounding on display. 或者,正如另一张海报所建议的那样,使用long long表示您的金额为千分之一欧元,并且只显示四舍五入。

A further possibility is to use units of half-cents ie 55 Euros 35 cents is represented internally as 11070 half cents then should not need to worry about rounding for any standard accounting transaction. 另一种可能性是使用半美分单位,即55欧元35美分,内部代表11070美分,然后不必担心任何标准会计交易的四舍五入。

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. 在我的上一个项目中,有超过一百页的业务规则处理利率计算,至少40页涉及计算的每个阶段的小数位数或要使用的舍入算法。

我建议使用十进制数字包,例如decNumber

If you are in windows and are willing to write managed C/C++. 如果您在Windows中并且愿意编写托管C / C ++。

In most cases, you can just use the decimal class . 在大多数情况下,您只需使用十进制 From MSDN 来自MSDN

The Decimal value type is appropriate for financial calculations requiring large numbers of significant integral and fractional digits and no round-off errors. Decimal值类型适用于需要大量有效积分和小数位且没有舍入误差的财务计算。

If you are not able or willing to use managed C/C++, look at decimal number package, such as decNumber . 如果您不能或不愿意使用托管C / C ++,请查看十进制数字包,例如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. 但是正如其他人所说的那样,你要小心翼翼。 Eg 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. 例如,在英国所得税软件规格中,大约一半的文本是关于在哪里以及如何(在每个地方不同)与圆形数字,因为你必须得到相同的结果,然后使用手动税表。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM