Python(奇怪的是)舍入值

Ped*_*sso 4 python floating-point numpy

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

我正在创建以下数组:

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]]
Run Code Online (Sandbox Code Playgroud)

然后,打印它:

for i,c in enumerate(A):
    for j,d in enumerate(c):
        print j, d
Run Code Online (Sandbox Code Playgroud)

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

>>>
0 0.6
1 0.4
0 0.4
1 0.6
Run Code Online (Sandbox Code Playgroud)

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

(0, 0.59999999999999998)
(1, 0.40000000000000002)
(0, 0.40000000000000002)
(1, 0.59999999999999998)
Run Code Online (Sandbox Code Playgroud)

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

Mar*_*son 16

这里有几件不同的事情.

首先,Python有两种将对象转换为字符串的机制,称为reprstr. repr应该给予"忠实"输出,这样可以(理想情况下)轻松地重新创建该对象,同时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
Run Code Online (Sandbox Code Playgroud)

但是,从您的角度来看,有点令人困惑,我们得到:

>>> x = 0.4
>>> str(x)
'0.4'
>>> repr(x)
'0.4'
Run Code Online (Sandbox Code Playgroud)

这似乎与你上面看到的不太相配,但我们将在下面回到这一点.

要记住的第二件事是,在你的第一个例子中,你打印两个单独的项目,而在你的第二个例子中(j删除),你打印一个项目:一个长度为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)
Run Code Online (Sandbox Code Playgroud)

这就解释了为什么你在两种情况下看到不同的结果,即使底层浮点数是相同的.

但是最后一块拼图.在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'>
Run Code Online (Sandbox Code Playgroud)

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

>>> 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'
Run Code Online (Sandbox Code Playgroud)

所以这三件事应该能够解释你所看到的结果.请放心,这一切都是完美的:底层浮点值不会以任何方式改变; 它只是由四个不同的可能组合显示方式不同str,并repr为两种类型:floatnumpy.float64.

Python教程提供了有关如何存储和显示Python浮动的更多详细信息,以及一些潜在的缺陷.对这些问题的答案这太问题对之间的区别的详细信息strrepr.