Floating Point Arithmetic - Modulo Operator on Double Type
所以我想知道为什么模运算符返回如此大的异常值。
如果我有密码:
它将给出
注意,使用除法运算不存在这个问题。-
将给出
让我明确一点:我对错误的存在并不感到惊讶,我对错误相对于游戏中的数字如此之大感到惊讶。0.0999~=0.1和0.1与
在下面的文章中,我已经阅读了关于stackoverflow的这个主题,其中包括一二三。
有人能解释为什么这个错误如此之大吗?任何避免在将来遇到问题的建议(我知道我可以用十进制代替,但我担心它的性能)。
编辑:我应该特别指出,我知道0.1是一个无限重复的二进制数字序列-这与它有什么关系吗?
出现错误的原因是double不能精确地表示0.1——它能表示的最接近的值是0.10000000005551115123126。现在,当你将1.0除以它时,它会给你一个略小于10的数字,但同样,一个双精度数不能精确地表示它,所以它最后会四舍五入到10。但是当你做mod的时候,它可以给你少于0.1的余数。
由于0=0.1 mod 0.1,因此mod中的实际错误为0.1-0.09999999…--非常小。
如果将%运算符的结果添加到9*0.1,它将再次给出1.0。
编辑
关于四舍五入的一些细节——特别是这个问题是混合精度危险的一个很好的例子。
浮点数的
因此,在本例中,您会看到一个明显的错误,因为您使用的是一个不连续的步骤函数(floor),这意味着内部值的微小差异可以将您推过步骤。但由于mod本身是一个不连续的步进函数,所以这里的实际错误是0.1-0.0999…因为0.1是mod函数范围内的不连续点。
0.1不能精确地用二进制表示这一事实与此有关。
如果0.1可以表示为
因为它不能,所以您得到的可表示的双精度数最接近于一个操作,该操作与您试图计算的操作完全不同。
还要注意,/是一个基本上连续的函数(参数上的小差异通常意味着结果上的小差异,虽然导数可以在零附近但在零的同一侧很大,但至少参数的额外精度有帮助)。另一方面,%不是连续的:无论您选择什么精度,总是会有参数,其中第一个参数上的任意小表示错误表示结果上的大错误。
按照指定IEEE754的方式,您只获得一个浮点运算结果的近似值的保证,前提是参数正是您想要的。如果参数不完全符合您的要求,则需要切换到其他解决方案,例如间隔算术或分析程序的良好条件(如果它对浮点数使用了%的值,则很可能没有很好的条件)。
这并不是计算中的一个"错误",而是你从来没有真正的0.1开始。
问题是1.0可以用二进制浮点精确表示,但0.1不能,因为它不能用2的负幂精确构造。(是1/16+1/32+…)
所以你不能得到1.0%0.1,机器只能计算1.0%0.1+-0.00…然后它如实地报告结果是什么…
为了有一个较大的余数,我假设
你看到的错误很小,乍一看就大。您的结果(显示舍入后)是当您期望从
所以你这里的实际错误是
根据您需要的号码,最好写下:
result = result >= 0.1/2 ? result - 0.1 : result;