使用不精确的表示来舍入浮点数

时间:2021-08-18 12:38:32

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.

基本上,我们想要的货币数量是1000000000.555到2个小数。然而,这个数字的浮点表示法是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中的十进制数据类型。

Any input would be helpful.

任何输入都是有用的。

Thanks, Rickard

谢谢你,理查德

4 个解决方案

#1


10  

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.

表示货币的正确方法通常是使用整数。例如,在欧元区,一种常见的方法是用小欧元(1 -6)来表示价值。显然你需要64位的数学运算。您将在整个应用程序中始终使用这个。只有在人类I/O上,你才会四舍五入,然后除以10000得到一个整数,单位是美分。

#2


5  

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.

钱的数额是不固定的,大多数财务和会计法规都承认这一点。你不可能以3.145217美元的价格来支付账单。在17世纪,人们可以把一枚硬币交给铁匠,然后让他把一枚银币切成小块(8个小块的披萨上有漂亮的小块标记来帮助这一过程!)

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.

例如,瑞士最小的硬币是5卢比,所以会计和票据必须用最近的5美分i来表示。如果你的供应商不太可能慷慨,你不可能收到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. Or as another poster suggested use long long representing your amounts in thousandths of Euros and only rounding on display.

你也有两个选择。获取boost BigDecimal库,它可以精确地指定测量结果。或者就像另一张海报建议的那样,用long long long来表示你的金额,用千分之一欧元表示,只在展示的时候用round表示。

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.

另一种可能是使用0.5美分的单位,即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页是关于计算的每一阶段的小数位数或要使用的四舍五入算法。

#3


4  

I recommend to use a decimal number package, such as decNumber.

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

#4


0  

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

在大多数情况下,您可以使用decimal类。从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.

如果您不能或不愿意使用托管的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.

在这两种情况下,您都应该有大量的单元测试和集成测试,这些测试覆盖了带有四舍五入的情况。他们会给你最大的痛苦。舍入也很擅长隐藏其他bug。

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.

然而,正如其他人所说的,在你周围要非常小心。E。在英国所得税软件规范中,大约有一半的文本是关于在哪里以及如何(在每个地方不同)整数的,因为你必须得到相同的结果,然后使用手工税法表。

#1


10  

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.

表示货币的正确方法通常是使用整数。例如,在欧元区,一种常见的方法是用小欧元(1 -6)来表示价值。显然你需要64位的数学运算。您将在整个应用程序中始终使用这个。只有在人类I/O上,你才会四舍五入,然后除以10000得到一个整数,单位是美分。

#2


5  

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.

钱的数额是不固定的,大多数财务和会计法规都承认这一点。你不可能以3.145217美元的价格来支付账单。在17世纪,人们可以把一枚硬币交给铁匠,然后让他把一枚银币切成小块(8个小块的披萨上有漂亮的小块标记来帮助这一过程!)

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.

例如,瑞士最小的硬币是5卢比,所以会计和票据必须用最近的5美分i来表示。如果你的供应商不太可能慷慨,你不可能收到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. Or as another poster suggested use long long representing your amounts in thousandths of Euros and only rounding on display.

你也有两个选择。获取boost BigDecimal库,它可以精确地指定测量结果。或者就像另一张海报建议的那样,用long long long来表示你的金额,用千分之一欧元表示,只在展示的时候用round表示。

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.

另一种可能是使用0.5美分的单位,即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页是关于计算的每一阶段的小数位数或要使用的四舍五入算法。

#3


4  

I recommend to use a decimal number package, such as decNumber.

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

#4


0  

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

在大多数情况下,您可以使用decimal类。从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.

如果您不能或不愿意使用托管的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.

在这两种情况下,您都应该有大量的单元测试和集成测试,这些测试覆盖了带有四舍五入的情况。他们会给你最大的痛苦。舍入也很擅长隐藏其他bug。

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.

然而,正如其他人所说的,在你周围要非常小心。E。在英国所得税软件规范中,大约有一半的文本是关于在哪里以及如何(在每个地方不同)整数的,因为你必须得到相同的结果,然后使用手工税法表。