Python(奇怪的是)舍入值

时间:2013-04-19 13:31:08

标签: python numpy floating-point

这个问题更多的是好奇心。

我正在创建以下数组:

A = zeros((2,2))
for i in range(2):
    A[i,i] = 0.6
    A[(i+1)%2,i] = 0.4
print A

>>>
   [[ 0.6  0.4]
   [ 0.4  0.6]]

然后,打印它:

for i,c in enumerate(A):
    for j,d in enumerate(c):
        print j, d

但是,如果我删除了j,我得到了:

>>>
0 0.6
1 0.4
0 0.4
1 0.6

但如果我从for中删除了j,我得到了:

(0, 0.59999999999999998)
(1, 0.40000000000000002)
(0, 0.40000000000000002)
(1, 0.59999999999999998)

这是因为我使用0.6创建矩阵的方式?它如何代表内部真实价值?

2 个答案:

答案 0 :(得分:16)

这里有一些不同的事情。

首先,Python有两种机制将对象转换为字符串,称为reprstrrepr应该提供“忠实”输出,这样可以(理想情况下)轻松地重新创建该对象,而str旨在提供更易读的输出。对于Python版本(包括Python 3.1)中的浮点数,repr给出足够的数字来完全确定浮点数的值(以便评估返回的字符串完全返回该浮点数),而str舍入为小数点后12位;这具有隐藏不准确性的效果,但意味着两个非常接近的不同浮点数最终可能具有相同的str值 - 这是repr无法实现的。打印对象时,将获得该对象的str。相反,当您只在解释器提示符处评估表达式时,您将获得repr

例如(这里使用Python 2.7):

>>> x = 1.0 / 7.0
>>> str(x)
'0.142857142857'
>>> repr(x)
'0.14285714285714285'
>>> print x  # print uses 'str'
0.142857142857
>>> x  # the interpreter read-eval-print loop uses 'repr'
0.14285714285714285

但是,从你的观点来看,有点混乱,我们得到:

>>> x = 0.4
>>> str(x)
'0.4'
>>> repr(x)
'0.4'

这似乎与你上面所看到的并不太好,但我们将在下面回到这一点。

要记住的第二件事是,在第一个示例中,您打印的是两个单独的项目,而在第二个示例中(删除了j),您打印的是单个项目:a长度为2的元组。有点令人惊讶的是,在使用str转换元组进行打印时,Python仍使用repr来计算该元组的元素的字符串表示形式:

>>> x = 1.0 / 7.0
>>> print x, x  # print x twice;  uses str(x)
0.142857142857 0.142857142857
>>> print(x, x)  # print a single tuple; uses repr(x)
(0.14285714285714285, 0.14285714285714285)

这就解释了为什么你会在这两种情况下看到不同的结果,即使底层花车是相同的。

但最后一块拼图。在Python> = 2.7中,我们在上面看到,对于特定的浮点0.4,该浮点数的strrepr是相同的。那么0.40000000000000002来自哪里?好吧,你没有Python浮点数:因为你从NumPy数组中获取这些值,它们实际上是numpy.float64类型的颜色:

>>> from numpy import zeros
>>> A = zeros((2, 2))
>>> A[:] = [[0.6, 0.4], [0.4, 0.6]]
>>> A
array([[ 0.6,  0.4],
       [ 0.4,  0.6]])
>>> type(A[0, 0])
<type 'numpy.float64'>

该类型仍然存储一个双精度浮点数,就像Python的浮点数一样,但它有一些额外的好处,使它与NumPy的其余部分很好地交互。事实证明,NumPy使用稍微不同的算法来计算repr的{​​{1}},而不是Python用于计算numpy.float64的{​​{1}}。 Python(版本&gt; = 2.7)旨在提供仍然提供浮点精确表示的最短字符串,而NumPy只是根据将基础值四舍五入为17位有效数字来输出字符串。回到上面的repr示例,这是NumPy所做的:

float

所以这三件事一起应该解释你所看到的结果。请放心,这一切都是完美的:底层浮点值不会以任何方式改变;对于两种类型0.4>>> from numpy import float64 >>> x = float64(1.0 / 7.0) >>> str(x) '0.142857142857' >>> repr(x) '0.14285714285714285' >>> x = float64(0.4) >>> str(x) '0.4' >>> repr(x) '0.40000000000000002' strrepr的四种不同可能组合的显示方式不同:floatnumpy.float64

Python教程给出了more details如何存储和显示Python浮点数,以及一些潜在的陷阱。 this SO question的答案提供了有关strrepr之间差异的更多信息。

答案 1 :(得分:3)

编辑:

别介意我,我没有意识到这个问题是关于NumPy的。


奇怪的0.59999999999999998和朋友是Python最准确地表示所有计算机如何存储浮点值的最佳尝试:根据IEEE 754标准,作为一堆比特。值得注意的是,0.1是二进制中的非终止小数,因此无法准确存储。 (因此,大概是0.60.4。)

您通常看到0.6的原因是大多数浮点打印功能使这些不精确存储的浮点数更加圆润,使我们人类更容易理解它们。这就是你的第一个印刷例子。

在某些情况下(也就是说,当打印功能不是为了人类可读时),将打印完整的略微关闭的数字0.59999999999999998。这就是你的第二个印刷例子。

TL;博士

这不是Python的错;它只是浮动的存储方式。