将数组转换为列表时出现数值错误

Numerical error when converting array to list

我在 Python 中使用 Numpy 和 ode 进行了相当多的科学数值积分。我使用了多个数组,我想将一维数组转换为列表以便导出和更容易操作。从那时起,我发现了更简单、更 pythonic 的方法,而无需求助于列表,但在此之前,我偶然发现了这种我找不到解释的奇怪的不需要的行为。我已经删除了结果中的数组,但这是全部代码。

如果我使用 arange

创建范围数组

a=numpy.arange(1,20,0.2)

结果是

array([ 1. , 1.2, 1.4, 1.6, ... , 19.4, 19.6, 19.8])

但是如果像

那样使用list()方法

list(a)

一下子就变成了

[1.0, 1.2, 1.3999999999999999, 1.5999999999999999, ..., 19.399999999999995, 19.599999999999994, 19.799999999999997]

这是一个巨大的错误。我不是专家,所以我不能确切地说出它是如何发生的。我已经向一个非常了解计算机中数字处理的特定主题的人展示了这个,他们告诉我这很好奇,但除了说

之外无法立即确定具体问题

Well, it's not really an error, because 0.2 can't be represented exactly, but is almost certainly not intended! All that is the case is that those two methods are not last-bit compatible

我在 Internet 上进行了搜索,但没有成功,既搜索了这个确切的错误,也使用了从上述解释中提取的关键字。所以我决定在这里问

以前有人遇到过这种错误吗?有没有人深入解释是什么原因造成的?

我在 CentOS 6.6 机器上的 Anaconda 环境中使用 Python 2.7.9。

编辑:显然Stack Exchange比我聪明,只提出了一个相关问题我已经写下了整个事物 Python - Converting an array to a list causes values to change

将数组转换为列表时不会引入数字错误,这只是浮点值在列表和数组中的表示方式不同。

调用 list(a) 意味着您获得了 NumPy 浮点类型的列表(不是 Python float 对象)。打印时,shell 打印浮点值的更多数字。默认情况下,NumPy 数组最多只打印浮点数的小数点后一位。

如果将 NumPy 数组的精度设置得更高,您将看到与列表中相同的值:

>>> np.set_printoptions(precision=16)
>>> a = np.arange(1, 20, 0.2)
>>> a[:10]
array([ 1.                ,  1.2               ,  1.3999999999999999,
        1.5999999999999999,  1.7999999999999998,  1.9999999999999998,
        2.1999999999999997,  2.3999999999999995,  2.5999999999999996,
        2.7999999999999998])

>>> list(a[:10])
[1.0,
 1.2,
 1.3999999999999999,
 1.5999999999999999,
 1.7999999999999998,
 1.9999999999999998,
 2.1999999999999997,
 2.3999999999999995,
 2.5999999999999996,
 2.7999999999999998]

这与 a.tolist() 的情况类似。在这里,NumPy 浮点数据类型被转换为列表的 Python float 对象(float 对象在内部只是 C double,就像 NumPy)。两种类型都将值近似为相同的精度并具有相同的表示怪癖。


顺便说一句,值得一提的是 linspace 生成这些类型的范围。您仍然会在浮点表示中看到 "inaccuracies",但与 arange 不同,您可以确定此函数 returns 端点恰好是:

>>> np.linspace(1, 20, 96)
array([  1.                ,   1.2               ,   1.3999999999999999,
         1.6000000000000001,   1.8               ,   2.                ,
         ...
         19.                ,  19.1999999999999993,  19.4000000000000021,
         19.6000000000000014,  19.8000000000000007,  20.                ])

如果您使用 repr 打印数组的各个内容,您将看到完全相同的输出:

import numpy

a = numpy.arange(1, 20, 0.2)

print(repr(a[2]))
1.3999999999999999

或使用 ipython shell:

n [2]: a = numpy.arange(1, 20, 0.2)

In [3]: a[3]
Out[3]: 1.5999999999999999

In [4]: a[2]
Out[4]: 1.3999999999999999

In [5]: a[4]
Out[5]: 1.7999999999999998

一个python列表显示存储在列表中的对象的repr表示,numpy像print in python格式化输出到一定的精度。