Python(奇怪的)舍入值
这个问题更多是出于好奇 我正在创建以下数组:Python(奇怪的)舍入值,python,numpy,floating-point,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,我得到:
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?它如何表示内部真实值?编辑:
别介意,我没意识到这个问题是关于NumPy的
奇怪的
0.59999999998
和friends是Python准确表示所有计算机如何存储浮点值的最佳尝试:根据IEEE 754标准,它是一组位。值得注意的是,0.1
是二进制的非终止十进制,因此无法准确存储。(大概是0.6
和0.4
)
您通常看到0.6
的原因是大多数浮点打印函数将这些存储不精确的浮点值舍入,以使我们更容易理解。这就是您的第一个打印示例所做的
在某些情况下(即,当打印功能无法让人阅读时),将打印完整的、稍有偏差的编号0.59999999998
。这就是您的第二个打印示例所做的
tl;博士
这不是Python的错;这只是浮点数的存储方式。这里发生了一些不同的事情 首先,Python有两种将对象转换为字符串的机制,分别称为
repr
和str
repr
应该提供“忠实”的输出,这将(理想情况下)使精确地重新创建该对象变得容易,而str
的目标是提供更具可读性的输出。对于Python3.1之前(包括Python3.1)的Python版本中的浮点值,repr
提供足够的数字以完全确定浮点值(以便对返回的字符串求值时准确返回该浮点值),而str
四舍五入到小数点后12位;这具有隐藏不准确信息的效果,但意味着两个非常接近的不同浮点值可能最终得到相同的str
值-这是repr
无法实现的。打印对象时,您将获得该对象的str
。相反,当您仅在解释器提示下计算表达式时,会得到repr
例如(这里使用Python 2.7):
但是,从你的角度来看,有点令人困惑,我们得到:
>>> x = 0.4
>>> str(x)
'0.4'
>>> repr(x)
'0.4'
这似乎与您在上面看到的不太吻合,但我们将在下面回到这一点
要记住的第二件事是,在第一个示例中,您正在打印两个单独的项,而在第二个示例中(去掉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)
这就解释了为什么在这两种情况下会看到不同的结果,即使基本浮动是相同的
但还有最后一块拼图。在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'>
因此,这三件事加在一起应该可以解释你看到的结果。请放心,这完全是装饰性的:底层浮点值没有以任何方式更改;对于两种类型:float
和numpy.float64
,通过四种可能的组合str
和repr
显示不同
Python教程给出了Python浮动的存储和显示方式,以及一些潜在的陷阱。答案是关于
str
和repr
之间差异的更多信息,但是为什么一个案例打印的是“人性化”版本,而另一个版本打印的是完全精确的?是的,我想是这样的。枚举(A)生成的类型是什么?Tuple也?@default.kramer我不知道这类事情的所有标准。可能是某个C函数的某个参数,几十年来没有人接触过。有关为什么有时表示法比其他表示法更人性化的问题,请参阅马克·迪金森的答案。(请注意,他是唯一有资格回答这个问题的人,因为他是Python的核心开发人员,如果不是对Python最新版本的循环和显示浮动的方式负主要责任的话。)@default.kramer:还要注意,具有大量数字的表示并没有比具有较少数字的表示更精确。由于精确数字在任何有限二进制系统中都不存在,因此长表示和短表示都是实际存储内容的近似值。您可以使用decimal.decimal
这将完全按照您的预期存储它们。它从2.5或2.6开始就在标准库中(我忘了是哪个了)。等等。“矩阵”?这是标准的Python还是NumPy或其他类似的东西,还是完全其他的东西?@michaelb958:他使用的是NumPy数组,所以显示的值不是Python浮点值;它们是NumPy.float64
类型的NumPy对象。加上str
和repr
之间的差异,再加上计算元组的str
使用项目的repr
,这就解释了这里发生的事情。@larsmans:我同意我们有很多类似的问题,但是,由于马克·迪金森(MarkDickinson)所指出的附加功能,这个特殊的版本有一个转折点。所以在这一次,我没有投票决定结束。我必须改变我的投票,这真的很有启发性
>>> 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'>
>>> 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'