这个问题更多的是好奇心。
我正在创建以下数组:
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创建矩阵的方式?它如何代表内部真实价值?
答案 0 :(得分:16)
这里有一些不同的事情。
首先,Python有两种机制将对象转换为字符串,称为repr
和str
。 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
但是,从你的观点来看,有点混乱,我们得到:
>>> 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
,该浮点数的str
和repr
是相同的。那么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'
,str
和repr
的四种不同可能组合的显示方式不同:float
和numpy.float64
。
Python教程给出了more details如何存储和显示Python浮点数,以及一些潜在的陷阱。 this SO question的答案提供了有关str
和repr
之间差异的更多信息。
答案 1 :(得分:3)
别介意我,我没有意识到这个问题是关于NumPy的。
奇怪的0.59999999999999998
和朋友是Python最准确地表示所有计算机如何存储浮点值的最佳尝试:根据IEEE 754标准,作为一堆比特。值得注意的是,0.1
是二进制中的非终止小数,因此无法准确存储。 (因此,大概是0.6
和0.4
。)
您通常看到0.6
的原因是大多数浮点打印功能使这些不精确存储的浮点数更加圆润,使我们人类更容易理解它们。这就是你的第一个印刷例子。
在某些情况下(也就是说,当打印功能不是为了人类可读时),将打印完整的略微关闭的数字0.59999999999999998
。这就是你的第二个印刷例子。
这不是Python的错;它只是浮动的存储方式。