Python舍入错误

Python round off error

本问题已经有最佳答案,请猛点这里访问。

我只是用Python做计算,我看到了这个

1
2
3
4
5
6
7
8
Python 2.7.3 (default, Aug  1 2012, 05:14:39)
[GCC 4.6.3] on linux2
Type"help","copyright","credits" or"license" for more information.
>>> 62 * 41.2 * 2 / 250
20.435200000000002
>>> 62 * 4.12 * 2 / 25
20.4352
>>>

只是为了确保我通过gcc触发了下面的代码,但这并没有显示出上面的行为。我知道这可能是一个四舍五入的问题,但它应该是统一的。为什么python在数学上不应该在后面加上00000000002呢?

C/C++代码

1
2
3
4
5
#include <stdio.h>
int main () {
  printf ("%lf %lf
"
, 62 * 41.2 * 2 / 250, 62 * 4.12 * 2 / 25);
}

结果

20.435200 20.435200

如果有人对特定的数字感兴趣,他们属于某些发动机排量计算比较:)


除了@ashwini chaudhary的回答,

问题:

为什么python在数学上不应该在后面加上00000000002

回答:

因为机器中的值不准确。它不是Python中的bug,也不是代码中的bug。您将在所有支持二进制浮点运算的语言中看到这个完全相同的问题。但是,不同的语言可能会显示不同的结果(舍入)。

试试这个:

1
0.1 + 0.2

你会得到

1
0.30000000000000004

这是因为0.1不能精确地用二进制形式表示。在基2或二进制中,0.1是无限重复数。

1
0.0001100110011001100110011001100110011001100110011...

所以,python决定把它取整。

我建议你在这里多读些。


别担心,C实际上也一样,你只是不打印它,因为你显示的数字太少:

1
2
3
4
5
#include <stdio.h>
int main () {
    printf ("%.16lf %.16lf
"
, 62 * 41.2 * 2 / 250, 62 * 4.12 * 2 / 25);
}

给予

1
20.4352000000000018 20.4351999999999983


在python中也使用print

1
2
3
4
5
In [87]: print 62 * 41.2 * 2 / 250
20.4352

In [88]: print 62 * 41.2 * 2 / 25
204.352

python shell使用repr来显示输出,这就是为什么您得到了奇怪的输出:

1
2
In [89]: print repr(62 * 41.2 * 2 / 250)
20.435200000000002

鉴于print使用str

1
2
In [90]: print str(62 * 41.2 * 2 / 250)
20.4352

尝试使用十进制

1
2
3
4
>>> from decimal import Decimal
>>> dec = Decimal('41.2')
>>> 62 * dec * 2 / 250
Decimal('20.4352')

请注意,必须使用字符串作为参数。如果使用float作为参数,它将失败。

1
2
3
4
5
6
>>> dec1 = Decimal(41.2)
>>> dec2 = Decimal('41.2')
>>> dec1 == dec2
False
>>> print dec1, dec2
41.2000000000000028421709430404007434844970703125 41.2


您将面临处理二进制浮点数的常见怪事;这些是大多数计算机语言所共有的。不同的输出舍入策略可以隐藏这些问题,但它们仍然存在。理解这些的通常参考是"每个计算机科学家都应该知道关于浮点运算的知识",但是在"浮点运算:问题和限制"中也有一个特定于python的指南。

python有几个解决方案,可以在需要时用于获得更精确的结果。第一个模块是decimal模块,它使用十进制数而不是二进制数。

1
2
3
4
5
>>> from decimal import Decimal
>>> Decimal(62) * Decimal("41.2") * Decimal(2) / Decimal(250)
Decimal('20.4352')
>>> Decimal(62) * Decimal("4.12") * Decimal(2) / Decimal(25)
Decimal('20.4352')

还有一个fractions模块,它跟踪一个独立的分子和分母,因此任何可能的分数都可以表示出来。

1
2
3
4
5
6
7
8
9
>>> from fractions import Fraction
>>> Fraction("41.2")
Fraction(206, 5)
>>> Fraction(62) * Fraction("41.2") * Fraction(2) / Fraction(250)
Fraction(12772, 625)
>>> Fraction(62) * Fraction("4.12") * Fraction(2) / Fraction(25)
Fraction(12772, 625)
>>> float(Fraction(12772, 625))
20.4352