简体   繁体   English

关于python的舍入误差和浮点数

[英]About python's rounding error and floating-point numbers

>>> 1/3
0.3333333333333333

>>> 1/3+1/3+1/3
1.0 

I can't understand why this is 1.0.我不明白为什么这是1.0。 Shouldn't it be 0.9999999999999999 ?不应该是0.9999999999999999吗? So I kind of came up with the solution that python has an automatic rounding for it's answer, but if than, the following results can't be explained...所以我想出了 python 有一个自动舍入的解决方案,但如果不是,以下结果无法解释......

>>> 1/3+1/3+1/3+1/3+1/3+1/3
1.9999999999999998

>>> (1/3+1/3+1/3)+(1/3+1/3+1/3)
2.0

I thought rounding error occurred because there were only limited number of digits to use in the mantissa, and the exponent,(in floating point numbers) but 0.9999~~9 is not off the limit of the number of digits too.. Can somebody explain why these results came out like this?我认为发生舍入错误是因为尾数中仅使用有限的位数,而指数(以浮点数表示)但 0.9999~~9 也没有超出位数的限制..有人可以解释一下为什么这些结果是这样的?

Most Python implementations use a binary floating-point format, most commonly the IEEE-754 binary64 format.大多数 Python 实现使用二进制浮点格式,最常见的是 IEEE-754 binary64 格式。 This format has no decimal digits.这种格式没有十进制数字。 It has 53 binary digits.它有 53 个二进制数字。

When this format is used with round-to-nearest-ties-to-even, computing 1/3 yields 0.333333333333333314829616256247390992939472198486328125.当此格式用于圆到最近的偶数时,计算 1/3 得到 0.333333333333333314829616256247390992939472198486328125。 Your Python implementation fails to show the full value by default;默认情况下,您的 Python 实现无法显示完整值; it shows “0.3333333333333333”, which misleads you.它显示“0.3333333333333333”,这会误导您。

When this number is added to itself, the result is 0.66666666666666662965923251249478198587894439697265625.将此数字添加到自身时,结果为 0.66666666666666662965923251249478198587894439697265625。 This result is exact;这个结果是准确的; it has no rounding error.它没有舍入误差。 (That is, it has no new rounding error; it is exactly the sum of 0.333333333333333314829616256247390992939472198486328125 with itself.) (也就是说,它没有新的舍入误差;它恰好是 0.333333333333333314829616256247390992939472198486328125 与自身之和。)

When 0.333333333333333314829616256247390992939472198486328125 is added again, the real-number result does not fit in 53 bits.当再次添加 0.333333333333333314829616256247390992939472198486328125 时,实数结果不适合 53 位。 So the result must be rounded.所以结果必须四舍五入。 This rounding happens to round upward, producing exactly 1.这种舍入恰好向上舍入,正好产生 1。

When 0.333333333333333314829616256247390992939472198486328125 is added again, the result again does not fit, and is rounded.当再次添加0.333333333333333314829616256247390992939472198486328125时,结果再次不适合,并被四舍五入。 This time, the rounding happens to be downward, and produces 1.3333333333333332593184650249895639717578887939453125.这一次,舍入恰好是向下的,并产生 1.3333333333333332593184650249895639717578887939453125。

Subsequent additions produce 1.666666666666666518636930049979127943515777587890625 and then 1.9999999999999997779553950749686919152736663818359375, which your Python implementation displays as “1.9999999999999998”. Subsequent additions produce 1.666666666666666518636930049979127943515777587890625 and then 1.9999999999999997779553950749686919152736663818359375, which your Python implementation displays as “1.9999999999999998”.

When you group the arithmetic as (1/3+1/3+1/3) + (1/3+1/3+1/3) , then 1 is obtained for each parenthesized item, as explained above, and 1+1 is of course 2.当您将算术分组为(1/3+1/3+1/3) + (1/3+1/3+1/3)时,如上所述,为每个带括号的项目获得 1,而 1+ 1当然是2。

This is one of the subtle points of IEEE-754 arithmetic.这是 IEEE-754 算法的微妙之处之一。 When you write:当你写:

>>> 1/3
0.3333333333333333

the number you see printed is a "rounded" version of the number that is internally stored as the result of 1/3 .您看到打印的数字是数字的“四舍五入”版本,内部存储为1/3的结果。 It's just what the Double -> String conversion in the printing process decided to show you.这正是打印过程中的 Double -> String 转换决定向您展示的内容。 But you already knew that.但你已经知道了。

Now you can ask, is there a way to find out what the difference is?现在你可以问,有没有办法找出区别是什么? Yes, use the fractions module:是的,使用fractions模块:

>>> from fractions import Fraction
>>> Fraction(1, 3) - Fraction(1/3)
Fraction(1, 54043195528445952)

Ah, that's interesting.啊,这很有趣。 So it is slightly less than the actual value, and the difference is 1 / 54043195528445952 .所以它比实际值略小,相差1 / 54043195528445952 This is, of course, expected.这当然是意料之中的。

So, what happens when you "add" two of these together.那么,当您将其中两个“添加”在一起时会发生什么。 Let's see:让我们来看看:

>>> Fraction(2,3) - Fraction(1/3+1/3)
Fraction(1, 27021597764222976)

Again, you're close to 2/3 rds, but still not quite there.再次,您接近2/3 rds,但仍不完全在那里。 Let's do the addition one more time:让我们再做一次加法:

>>> Fraction(1,1) - Fraction(1/3+1/3+1/3)
Fraction(0, 1)

Bingo, with 3 of them, the representation is exactly 1 . Bingo,其中 3 个,表示正好是1

Why is that?这是为什么? Well, in each addition you get a number that's close to what you think the answer should be, but the internal rounding causes the result to become a close-by number that's not what you had in mind.好吧,在每次加法中,您都会得到一个接近于您认为答案应该是的数字,但是内部四舍五入导致结果变成一个接近的数字,这不是您的想法。 With three additions what your intuition tells you and what the internal rounding does match up.通过三个添加,您的直觉告诉您什么以及内部四舍五入确实匹配。

It is important to emphasize that the addition 1/3 + 1/3 + 1/3 does not produce a 1 ;需要强调的是,加法1/3 + 1/3 + 1/3不会产生1 it just produces an internal value whose closest representation as an IEEE-754 double-precision floating point value is 1 .它只产生一个内部值,其最接近 IEEE-754 双精度浮点值的表示为1 This is a subtle but important difference.这是一个微妙但重要的区别。 Hope that helps!希望有帮助!

This question may provide some answers to the floating point error Is floating point math broken?这个问题可能会为浮点错误提供一些答案是浮点数学坏了吗?

With the brackets, the compiler is breaking down the addition into smaller pieces, reducing the possibility of a floating point which is not supposed to be there to carry on and keep 'accumulating' the floating point error.使用括号,编译器将加法分解为更小的部分,从而减少不应该存在的浮点的可能性并继续“累积”浮点错误。 Most likely when split into groups of 3, the compiler knows the sum will be 1 and add 1 + 1 together最有可能分成 3 组时,编译器知道总和将为 1 并将 1 + 1 加在一起

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

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