简体   繁体   English

为什么(0.0006 * 100000)%10是10

[英]why (0.0006*100000)%10 is 10

当我在python中执行(0.0006 * 100000)%10和(0.0003 * 100000)%10时它分别返回9.999999999999993,但实际上它必须为0.类似地,在c ++中,fmod(0.0003 * 100000,10)给出的值为10。有人可以帮我解决我出错的地方。

The closest IEEE 754 64-bit binary number to 0.0003 is 0.0002999999999999999737189393389513725196593441069126129150390625. 最接近的IEEE 754 64位二进制数为0.0003是0.0002999999999999999737189393389513725196593441069126129150390625。 The closest representable number to the result of multiplying it by 100000 is 29.999999999999996447286321199499070644378662109375. 将其乘以100000的结果的最接近的可表示数字是29.999999999999996447286321199499070644378662109375。

There are a number of operations, such as floor and mod, that can make very low significance differences very visible. 有许多操作,例如floor和mod,可以使非常低的显着性差异非常明显。 You need to be careful using them in connection with floating point numbers - remember that, in many cases, you have a very, very close approximation to the infinite precision value, not the infinite precision value itself. 您需要小心使用它们与浮点数相关联 - 请记住,在许多情况下,您对无限精度值有非常非常接近的近似,而不是无限精度值本身。 The actual value can be slightly high or, as in this case, slightly low. 实际值可能略高,或者在这种情况下略低。

Just to give the obvious answer: 0.0006 and 0.0003 are not representable in a machine double (at least on modern machines). 只是给出明显的答案: 0.00060.0003在机器双倍中是不可表示的(至少在现代机器上)。 So you didn't actually multiply by those values, but by some value very close. 所以你实际上并没有乘以这些值,而是通过一些非常接近的值。 Slightly more, or slightly less, depending on how the compiler rounded them. 略多或略少,取决于编译器如何舍入它们。

May I suggest using the remainder function in C? 我可以建议在C中使用余数函数吗?

It will compute the remainder after rounding the quotient to nearest integer, with exact computation (no rounding error): 它会在将商舍入到最接近的整数后计算余数,并进行精确计算(无舍入误差):

remainder = dividend - round(dividend/divisor)*divisor

This way, your result will be in [-divisor/2,+divisor/2] interval. 这样,您的结果将在[-divisor/2,+divisor/2]间隔内。
This will still emphasize the fact that you don't get a float exactly equal to 6/10,000 , but maybe in a less surprising way when you expect a null remainder: 这仍然会强调这样一个事实,即你没有得到一个完全等于6 / 10,000的浮点数,但是当你期望一个空余数时,可能会以一种不太令人惊讶的方式:

remainder(0.0006*100000,10.0) -> -7.105427357601002e-15
remainder(0.0003*100000,10.0) -> -3.552713678800501e-15

I don't know of such remainder function support in python, but there seems to be a match in gnulib-python module (to be verified...) 我不知道python中有这样的余数函数支持,但gnulib-python模块中似乎有匹配(待验证...)
https://github.com/ghostmansd/gnulib-python/blob/master/modules/remainder https://github.com/ghostmansd/gnulib-python/blob/master/modules/remainder

EDIT Why does it apparently work with every other N/10,000 in [1,9] interval but 3 and 6? 编辑为什么它显然适用于[1,9]间隔中的每隔一个N / 10,000但是3和6?

It's not completely lucky, this is somehow good properties of IEEE 754 in default rounding mode (round to nearest, tie to even). 这并不完全是幸运的,这在某种程度上是IEEE 754在默认舍入模式下的良好属性(舍入到最近,连接到偶数)。

The result of a floating point operation is rounded to nearest floating point value. 浮点运算的结果四舍五入到最接近的浮点值。
Instead of N/D you thus get (N/D+err) where the absolute error err is given by this snippet (I'm more comfortable in Smalltalk, but I'm sure you will find equivalent in Python): 而不是N / D你得到(N / D +错误),其中绝对错误错误是由这个片段给出的(我在Smalltalk中更舒服,但我相信你会在Python中找到相同的东西):

| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) asFloat]

It gives you something like: 它给你的东西:

#(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)

Changing the last bit of a floating point significand leads to a small difference named the unit of least precision (ulp), and it might be good to express the error in term of ulp: 更改浮点有效位的最后一位会导致一个小的差异,称为最小精度(ulp)的单位,并且可以用ulp表示错误:

| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) / (n/d) asFloat ulp]

the number of ulp off the exact fraction is thus: 因此,精确分数的ulp数量为:

#(0.3536 0.3536 -0.4848 0.3536 0.096 -0.4848 -0.0656 0.3536 -0.2272)

The error is the same for N=1,2,4,8 because they are essentially the same floating point - same significand, just the exponent changes. N = 1,2,4,8的误差是相同的,因为它们基本上是相同的浮点 - 相同的有效数,只是指数的变化。
It's also the same for N=3 and 6 for same reason, but very near the maximum error for a single operation which is 0.5 ulp (unluckily the number can be half way between two floats). 由于同样的原因,对于N = 3和6也是相同的,但是非常接近单个操作的最大误差 ,即0.5 ulp (不幸的是,这个数字可以是两个浮点数的一半)。
For N=9, the relative error is smaller than for N=1, and for 5 and 7, the error is very small. 对于N = 9,相对误差小于N = 1,并且对于5和7,误差非常小。

Now when we multiply these approximation by 10000 which is exactly representable as a float, (N/D+err) D is N+D err, and it's then rounded to nearest float. 现在,当我们将这些近似值乘以10000时,它可以精确地表示为浮点数,(N / D + err) D是N + D err,然后它舍入到最接近的浮点数。 If D*err is less than half distance to next float, then this is rounded to N and the rounding error vanishes. 如果D * err小于下一个浮点的一半距离,则将其舍入为N并且舍入误差消失。

| d |
d := 10000.
^(1 to: 9) collect: [:n | ((n/d) asFloat asFraction - (n/d)) * d / n asFloat ulp]

OK, we were unlucky for N=3 and 6, the already high rounding error magnitude has become greater than 0.5 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)

Beware, the distance is not symmetric for exact powers of two, the next float after 1.0 is 1.0+2^-52, but before 1.0 it's 1.0-2^-53. 注意,对于精确的2的幂,距离是不对称的,1.0之后的下一个浮点数是1.0 + 2 ^ -52,但是在1.0之前它是1.0-2 ^ -53。

Nonetheless, what we see here, is that after the second rounding operation, the error did annihilate in four cases , and did cumulate only in a single case (counting only the cases with different significands). 尽管如此,我们在这里看到的是,在第二次舍入操作之后,错误确实在四种情况下消失 ,并且仅在一种情况下累积 (仅计算具有不同有效数的情况)。

We can generalize that result. 我们可以推广这个结果。 As long as we do not sum numbers with very different exponents, but just use muliply/divide operations, while the error bound can be high after P operations, the statistical distribution of cumulated errors has a remarkably narrow peak compared to this bound, and the result are somehow surprisingly good wrt what we regularly read about float imprecision. 只要我们不对具有非常不同指数的数字求和,而只是使用多次/除法运算,而在P运算之后误差界限可能很高,累积误差的统计分布与此界限相比具有非常窄的峰值,并且结果在某种程度上是令人惊讶的好,我们经常阅读有关浮动不精确的内容。 See my answer to The number of correct decimal digits in a product of doubles with a large number of terms for example. 有关具有大量术语的双精度乘积中的正确小数位数,请参阅我的答案。

I just wanted to mention that yes, float are inexact, but they sometimes do such a decent job, that they are fostering the illusion of exactness. 我只是想提一下,是的,浮动是不精确的,但他们有时会做这么好的工作,他们正在培养精确的错觉。 Finding a few outliers like mentionned in this post is then surprising. 找到这篇文章中提到的一些异常值然后令人惊讶。 The sooner surprise, the least surprise. 越快意外,最不出意外。 Ah, if only float were implemented less carefully, there would be less questions in this category... 啊,如果只是不太谨慎地实施浮动,那么这个类别中的问题会更少......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM