复数的 Numpy 小数点精度

Numpy decimal points precision of complex numbers

考虑以下 Python 代码,它对 Python 中的复数进行一些简单的算术运算:

import numpy as np

s = 2
l = 5
v = np.array([np.exp(1j*2*np.pi/l)])
A = pow(s*v, l) + s*v

#Print the precision of np.complex128
print np.finfo(np.complex128).precision

#Export using 20 decimal places for real and imaginary parts
np.savetxt('A.csv', A, fmt=['%.20e%+.20ej'], delimiter=',')

我看到这个 answer 是为了在 Python 中打印 np.complex128 变量的精度,我得到的是 15。当我在 Spyder 中检查 A 的值时,我参见 (32.61803398874989+1.9021130325903035j) 其虚部超过 15 位小数。此外,导出的 CSV 文件的值为 3.26180339887498931262e+01+1.90211303259030350965e+00j 其实部和虚部都有 20 有用 个小数位。

我在这里很困惑:如果精度是 15,那些多余的小数位是多少?根据herenp.complex128由两个64位的浮点数组成,一个是实部,一个是虚部。

但我的主要问题是,我在程序中对复数(如加法、乘法和矩阵求逆)进行了许多此类运算,每次 运行 都会得到不同的结果,有时是正确的,有时是错误的。看来我的程序对这些操作的准确性非常敏感。 Python 中复数的精度是如何量化的?我能达到的最大值是多少?

我正在使用 Python 2.7.14 和 Numpy 1.14.3。

完整的答案需要很多 space。对于初学者,您需要阅读一本关于数值分析的书,该书解释了二进制浮点类型的工作和存储方式。但这里有一些部分的解释。

64 位浮点值以二进制形式存储,而不是十进制形式。所以大部分关于十进制数字的说法必须是近似值,而不是完整的故事。

np.finfo(np.complex128).precision(或其他任何内容)表示有 15 位有效小数位时,这意味着您不能指望超过这 15 位数字。这是一个示例,取自我的 IPython 控制台。

In [4]: 9.000000000000001
Out[4]: 9.000000000000002

In [5]: 9.000000000000001 == 9.000000000000002
Out[5]: True

Python 默认情况下对任何带小数点的数字使用 64 位浮点类型,将这两个 16 位小数数字视为相同。当您使用数字 9.000000000000001 时,只有前 15 个十进制数字可以保证正确存储。之后的任何事情都无法保证,在这种情况下,您会看到 1 基本上更改为 2.

有时您可以获得比这 15 位小数更多的数字。例如,由于数字是以二进制形式存储的,因此略大于 1.0 的数字在 radix point 之后的二进制数字将比 9.0 这样的数字多。这是因为9使用了4位二进制数,而1只使用了1位,所以小数点后可以再使用3位二进制数。那么让我们看看你的数字的虚部,1.9021130325903035:

In [17]: 1.902113032590303 == 1.9021130325903035
Out[17]: False

In [18]: 1.9021130325903036 == 1.9021130325903035
Out[18]: True

In [19]: 1.902113032590304 == 1.9021130325903035
Out[19]: False

我们看到,虽然数字显示 17 位十进制数字,但当我们将最后一位数字从 5 更改为 6 时,Python 没有看到任何变化。但是,如果我们将该数字向上或向下舍入为 16 位十进制数字,则会发生变化。所以我们可以说该数字存储为 16 位十进制数字甚至更多。这就是为什么当我解释精度时,我说浮点数保证有 15 位有效小数位,但它可能有 16 位,而实际精度略高于此。更简单地说,有 15 或 16 位有效的小数位。

Python 有两种打印浮点数的基本方法:str 函数和 repr 函数。 str 函数打印内容很简单,因此用户无需深入了解细节即可理解结果。 repr函数给出了更多的细节,并试图打印如此多的细节,以至于存储的数据可以完全由打印的内容决定。请注意,repr 在某些情况下是不可见的,例如在控制台中键入数值,或者如您所见,在 Spyder 中使用变量资源管理器。

当 Spyder 或 Python 对您的号码 1.9021130325903035 执行 repr 时,它会提供足够的数字来完整定义号码。正如我们在上面看到的,如果它只显示 16 位十进制数字,去掉最后的 5,结果将与存储的略有不同。因此 Python 打印了一个额外的数字,这样你就可以知道这个值是多少。如果 Python 打印了最后的 6 而不是 5 ,则值将是相同的,但如果 Python 完全省略了该数字,则该值将被更改。所以 Python 通过 repr 错误的是给了太多数字。尽管打印了 17 个数字,但其中只有 16 个数字是确定的。

最后,由于 np.savetxt 命令中的 %.20e 说明符,您的 csv 文件显示了 20 个十进制数字,因为您告诉 Python 显示 20 个十进制数字。这 20 位小数并不都是“有用的 小数位”,不管你写了什么。只有前 16 或 17 个在虚数部分有用,具体取决于您如何定义 "useful." Python 基本上是将二进制位全零添加到存储值,并将其打印为 20 位十进制数字。但是那些零二进制位没有存储在值中。