浮点算法 - 双类型的模算子

Cri*_*onX 9 c# floating-point precision

所以我试图弄清楚为什么模运算符返回如此大的异常值.

如果我有代码:

double result = 1.0d % 0.1d;

它会给出一个结果0.09999999999999995.我期望值的价值0

注意使用除法运算符不存在此问题 - double result = 1.0d / 0.1d;

将给出结果10.0,意味着剩下的应该0.

让我明确一点:对于存在错误我并不感到惊讶,我很惊讶这个错误与游戏中的数字相比非常.0.0999~ = 0.1和0.1处于相同数量级0.1d并且仅相差一个数量级1.0d.它不像你可以将它与double.epsilon相比,或者说"如果它的<0.00001差异则相等".

我已经在StackOverflow上阅读了这个主题,在下面的帖子中 三个,其中包括.

任何人都可以建议解释为什么这个错误如此之大?任何建议,以避免将来遇到问题(我知道我可以使用十进制,但我担心它的性能).

编辑:我应该特别指出,我知道0.1是一个无限重复的二进制数字系列 - 这与它有什么关系吗?

Chr*_*odd 14

出现错误是因为double不能精确地表示0.1 - 它可以表示的最接近的值是0.100000000000000005551115123126.现在,当你将1.0除以它时,它会给你一个略小于10的数字,但是双倍不能完全代表它,所以它最终会向上舍入到10.但是当你执行mod时,它可以稍微给你一点少于0.1余数.

因为0 = 0.1 mod 0.1,mod中的实际误差为0.1 - 0.09999999 ...... - 非常小.

如果将%运算符的结果添加到9*0.1,它将再次给出1.0.

编辑

关于舍入内容的更多细节 - 特别是因为这个问题是混合精度危险的一个很好的例子.

a % b通常计算浮点数的方式为a - (b * floor(a/b)).问题是它可以一次性完成,内部精度高于你在这些操作中获得的内部精度(并将结果四舍五入到每个阶段的fp数),因此它可能会给你一个不同的结果.很多人看到的一个例子是英特尔x86/x87硬件,它使用80位精度进行中间计算,内存值只使用64位精度.所以价值在b在上面的等式中来自内存,因此是一个64位的fp数字,它不是0.1(感谢dan04的确切值),所以当它计算1.0/0.1时它得到9.9999999999999944488848768742172978818416595458984375(四舍五入到80位).现在,如果你将它舍入到64位,它将是10.0,但是如果你保持80位内部并在其上做底线,它会截断为9.0,因此得到.0999999999999999500399638918679556809365749359130859375作为最终答案.

因此,在这种情况下,您会看到一个很大的明显错误,因为您使用的是非连续步长函数(floor),这意味着内部值的微小差异可以推动您超越该步骤.但由于mod本身是一个非连续的阶跃函数,这是预期的,这里的实际误差是0.1-0.0999 ...因为0.1是mod函数范围内的不连续点.