我的团队正在使用财务软件,它将货币价值暴露为C#浮点数的两倍。有时,我们需要比较这些值,看它们是否等于零,或者是否属于特定限制。当我注意到这个逻辑中的意外行为时,我很快就了解了浮点双精度中固有的舍入误差(例如1.1 + 2.2 = 3.3000000000000003)。到目前为止,我主要使用C#小数来表示货币价值。
我的团队决定使用epsilon值方法解决此问题。基本上,当你比较两个数字时,如果这两个数字之间的差异小于epsilon,它们被认为是相等的。我们以下面文章中描述的类似方式实现了这种方法: https://www.codeproject.com/Articles/383871/Demystify-Csharp-floating-point-equality-and-relat
我们的挑战是确定epsilon的合适值。我们的货币值最多可以包含小数点右侧的3位数(比例= 3)。这意味着我们可以使用的最大epsilon是.0001(任何更大的东西都会被忽略)。由于epsilon值应该很小,我们决定将它再移出一个小数点到.00001(只是为了安全,你可以说)。 C#双精度具有at least 15 digits的精度,所以我相信如果小数点左边的数字小于或等于10位数(15 - 5 = 10,其中5是数字,则epsilon的值应该有效)数字epsilon位于小数点右侧)。使用10位数字,我们可以将数值表示为数十亿,最高可达9,999,999,999.999。我们可能有数亿的数字,但我们不希望达到数十亿,所以这个限制就足够了。
我选择这个epsilon值的理由是正确的吗?我找到了很多讨论这种方法的资源,但我找不到很多资源来提供选择epsilon的指导。
答案 0 :(得分: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美元...会计师喜欢“脚”的东西。