2017-10-09 23 views
2

我的团队正在使用将货币值暴露为C#浮点加倍的财务软件。偶尔,我们需要比较这些值,看看它们是否等于零,或者是否落在特定的限制之下。当我注意到这个逻辑出现意外的行为时,我很快就了解了浮点双精度所固有的舍入误差(例如1.1 + 2.2 = 3.3000000000000003)。到目前为止,我主要使用C#小数来表示货币值。为浮点比较选择一个Epsilon值

我的团队决定使用epsilon值方法解决此问题。基本上,当你比较两个数字时,如果这两个数字之间的差值小于ε,那么它们被认为是相等的。我们实施以类似的方式这种方式为下面的文章中描述: https://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat

我们面临的挑战已经确定小量适当的值。我们的货币值在小数点右侧最多可以有3位数字(比例= 3)。这意味着我们可以使用的最大epsilon是.0001(任何更大的数字和第三位被忽略)。由于epsilon的值应该很小,我们决定将它移出一个小数点到.00001(为了安全起见,你可以说)。 C#双精度为at least 15 digits,所以我相信如果小数点左边的数字小于或等于10位数(15 - 5 = 10,其中5是小数位数为在小数点右侧)。用10位数字,我们可以将数值表示为数十亿,最高可达9,999,999,999.999。我们可能有数以亿计的数字,但我们并不期望达到数十亿,所以这个限制应该足够了。

我选择epsilon的这个值的理由是正确的吗?我发现了很多讨论这种方法的资源,但我找不到提供选择epsilon指导的许多资源。

+2

我认为使用正常的,甚至DP,IEEE754不是一个伟大的财务计算思路。感知的智慧是使用C#的[decimal](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/decimal)类型;你考虑过这个吗? –

+0

@IsaacWoods我正在为现成的财务软件编写插件代码,所以我无法控制它正在使用的数据类型。我曾考虑将double值转换为小数,但如果double已经包含舍入错误,它只会被复制到小数点。 –

+1

由于经历了一些痛苦的经历,从一位经理告诉我说:“你永远不会读200多张打卡。“,”我们预计不会进入数十亿“这一形式的条款让我非常紧张,你将如何执行这一限制?如果存在一段时间的通货膨胀,会发生什么情况?你以一家大型银行作为客户?您的代码与其他货币一起使用? –

回答

2

你的推理看起来很合理,但正如你已经发现它是一个复杂的问题。您可能想要阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic。您使用64位双打确实有一个精度为15位的至少。但是,您还需要验证输入,因为浮点数可以包含Nan,+/- Infinity,负数零以及比15位十进制数字大得多的“范围”。如果某人给你的图书馆赋予了一个像1.2E102这样的值,你应该处理它还是将其视为超出范围?同样值很小。垃圾进入,垃​​圾出来,但如果你的代码检测到垃圾的“味道”并且至少记录了垃圾,它可能会很好。

您可能还想考虑提供一个属性来设置精度以及不同形式的舍入。这在很大程度上取决于您正在使用的规格。您可能还想确定这些值是否可以代表美元以外的货币(1美元目前> 112日元)。

长和短的选择你的需求下一个数字(因此小数点右边的四位数字)你的epsilon是健全的,并给你一个数字用于一致的四舍五入。否则10.0129美元和10.0121美元将是平等的,但他们的总额将是20.025美元,而不是20.024美元......会计师喜欢“脚”的东西。