当我在python中执行(0.0006 * 100000)%10和(0.0003 * 100000)%10时,它分别返回9.999999999999993,但实际上它必须为0。 类似地,在c ++中,fmod(0.0003 * 100000,10)给出的值为10.有人可以帮我解决我出错的地方。
答案 0 :(得分:10)
最接近的IEEE 754 64位二进制数为0.0003是0.0002999999999999999737189393389513725196593441069126129150390625。将它乘以100000的结果的最接近的可表示数字是29.999999999999996447286321199499070644378662109375。
有许多操作,例如floor和mod,可以使非常低的显着性差异非常明显。您需要小心使用它们与浮点数相关联 - 请记住,在许多情况下,您对无限精度值有非常非常接近的近似,而不是无限精度值本身。实际值可能略高,或者在这种情况下略低。
答案 1 :(得分:4)
只是给出明显的答案:0.0006
和0.0003
在机器双倍中是不可表示的(至少在现代机器上)。所以你实际上并没有乘以这些值,而是通过一些非常接近的值。略多或略少,取决于编译器如何舍入它们。
答案 2 :(得分:0)
我可以建议在C中使用余数函数吗?
它将计算舍入之后的余数为最接近的整数,精确计算(无舍入误差):
remainder = dividend - round(dividend/divisor)*divisor
这样,您的结果将在[-divisor/2,+divisor/2]
区间内
这仍然会强调这样一个事实,即你没有得到一个完全等于6 / 10,000的浮点数,但是当你期望一个空余数时,可能会以一种不太令人惊讶的方式:
remainder(0.0006*100000,10.0) -> -7.105427357601002e-15
remainder(0.0003*100000,10.0) -> -3.552713678800501e-15
我不知道python中有这样的余数函数支持,但gnulib-python模块中似乎有匹配(待验证...)
https://github.com/ghostmansd/gnulib-python/blob/master/modules/remainder
修改强> 为什么它显然适用于[1,9]间隔中的每隔一个N / 10,000但是3和6?
这并不是完全幸运,这在某种程度上是IEEE 754在默认舍入模式下的良好属性(舍入到最近,连接到偶数)。
浮点运算的结果四舍五入到最接近的浮点值 而不是N / D你得到(N / D +错误),其中绝对错误错误是由这个片段给出的(我在Smalltalk中更舒服,但我相信你会在Python中找到相同的东西):
| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) asFloat]
它为您提供了类似的内容:
#(4.79217360238593e-21 9.58434720477186e-21 -2.6281060661048628e-20 1.916869440954372e-20 1.0408340855860843e-20 -5.2562121322097256e-20 -7.11236625150491e-21 3.833738881908744e-20 -2.4633073358870662e-20)
更改浮点有效数的最后一位导致一个小的差异,命名为精度最低(ulp),并且用ulp表示错误可能是好的:
| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) / (n/d) asFloat ulp]
因此,精确分数的ulp数是:
#(0.3536 0.3536 -0.4848 0.3536 0.096 -0.4848 -0.0656 0.3536 -0.2272)
N = 1,2,4,8的误差是相同的,因为它们基本上是相同的浮点 - 相同的有效数,只是指数的变化。
由于同样的原因,对于N = 3和6也是相同的,但是对于 0.5 ulp 的单个操作非常接近最大误差(不幸的是这个数字可能是中途在两个花车之间)
对于N = 9,相对误差小于N = 1,对于5和7,误差非常小。
现在,当我们将这些近似值乘以10000时,它可以精确地表示为浮点数,(N / D +错误) D是N + D 错误,然后它会舍入到最接近的浮点数。如果D * err与下一个float的距离小于一半,则将其四舍五入为N并且舍入误差消失。
| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) * d / n asFloat ulp]
好吧,我们不幸的是N = 3和6,已经很高的舍入误差幅度已经大于0.5 ulp:
#(0.2158203125 0.2158203125 -0.591796875 0.2158203125 0.1171875 -0.591796875 -0.080078125 0.2158203125 -0.138671875)
注意,对于精确的2的幂,距离不对称,1.0之后的下一个浮点数是1.0 + 2 ^ -52,但是在1.0之前它是1.0-2 ^ -53。
尽管如此,我们在这里看到的是,在第二次舍入操作之后,错误确实在四个案例中歼灭,并且仅在一个案例中累积(计数只有具有不同有效数的情况。)
我们可以推广这个结果。只要我们不对具有非常不同指数的数字求和,而只是使用多次/除法运算,而在P运算之后误差界限可能很高,累积误差的统计分布与此界限相比具有非常窄的峰值,并且结果在某种程度上令人惊讶地好我们经常读到关于漂浮不精确的内容。例如,请参阅我对The number of correct decimal digits in a product of doubles with a large number of terms的回答。
我只是想提一下,是的,漂浮是不精确的,但他们有时会做这么好的工作,他们正在培养精确的错觉。找到这篇文章中提到的一些异常值然后令人惊讶。越快意外,最不出意外。啊,如果只是不太谨慎地实施浮动,那么这个类别的问题会更少......