【问题标题】:Numpy decimal points precision of complex numbers复数的numpy小数点精度
【发布时间】:2018-11-04 06:09:27
【问题描述】:

考虑以下 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。

【问题讨论】:

  • 您是如何“检查 Spyder 中 A 的值”的?有很多方法可以做到这一点。
  • @RoryDaulton 我刚刚使用了变量资源管理器并双击了变量。

标签: python numpy math precision complex-numbers


【解决方案1】:

完整的答案会占用很多空间。对于初学者,您需要阅读一本关于数值分析的书,其中解释了二进制浮点类型如何工作和存储。但这里有一些部分解释。

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 个十进制数字。例如,由于数字是以二进制形式存储的,因此比 9.0 稍大的数字在 radix point 之后的二进制数字会更多。这是因为9 使用了 4 个二进制数字,而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 个是确定的。

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

【讨论】:

  • 我明白你的意思,但我有一些疑问。当我说“有用的”小数位时,我的意思是打印在 CSV 上的额外数字不为零,因此根据您的回答,因为实际值保持不变,因此可以假设它们是随机数。但你为什么声称它们是零?
  • 我没有声称打印的那些额外的十进制数字是零;我声称附加到存储值的额外 binary 数字是零。换句话说,Python 假装存储的浮点值是exact 并打印前 20 个十进制数字。虚部的精确值(打印所有十进制数字)是1.9021130325903035096501980660832487046718597412109375,它有 53 个十进制数字。 (我使用了decimal 模块来获得它。)CSV 得到了前 20 个十进制数字。那些添加的数字不是“随机数”。
  • 好的,我明白了。但是,我的算法有时似乎有效。准确地说,一个 4×4 复数 Vandermonde 矩阵的行有 24 种排列,我在算法过程中对其进行了反转。在此算法中,所有排列都应给出相同的最终结果。我不会弄乱细节,但问题是对于某些排列它有效,而对于某些排列则无效,我几乎可以肯定这是一个精度问题,也许是在那些有问题的情况下计算逆时。你知道我是否可以提高 Python 中复数和/或复杂运算的精度吗?
  • @mgus:在 Python 中扩展浮点精度的常用方法是 decimal 模块,但它只处理实数,而不是复数。 mpmath module 执行“具有任意精度的实数和复数浮点运算”。它不包含在标准 Python 中,但默认情况下包含并安装在 Python 的 Anaconda 安装中(这是我使用的)。你可能想检查你的矩阵求逆例程——有很多,有些比其他的更精确。 mpmath 确实根据其功能列表进行矩阵求逆。
  • 我还有一个问题。一般来说,您从哪里开始计算这 15 个保证小数点的数量?这里有 2 个示例 1000.1234567891234561.000123456789123456e3。这些数字完全相同,但即使我将倒数第二个数字更改为第一个数字,我的 Python 也会打印它们是相等的。我对这 15 个小数点感到很困惑。似乎这里不能保证它们。
猜你喜欢
  • 2021-11-11
  • 1970-01-01
  • 1970-01-01
  • 2013-01-10
  • 1970-01-01
  • 2015-03-14
  • 2013-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多