4

我试图在这里找出模式:

>>> 1e300 ** 2
OverflowError: (34, 'Result too large')
>>> 1e300j ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1j) ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1e300j) ** 2
(nan+nanj)

该行为似乎不仅在理论上未指定,而且在实践中也很奇怪!这是什么解释?

4

3 回答 3

7

查看复数幂运算的来源表明 Python 仅在计算结束时检查溢出。此外,对于使用平方取幂的小整数指数,还有一种特殊情况,其中涉及复数乘法。

r.real = a.real*b.real - a.imag*b.imag;
r.imag = a.real*b.imag + a.imag*b.real;

这是复数乘法的公式。请注意以下事项:

a.real*b.real - a.imag*b.imag

ab非常大时,这变为浮点无穷大减去浮点无穷大,即nan. 结果nan传播,经过几次操作,结果为(nan+nanj)Py_ADJUST_ERANGE2仅当它看到无穷大时才设置errno,因此它会错过溢出并继续前进。

总之,Python只检查最终结果是否有溢出,而不是中间值,这会导致它错过了中间的溢出,因为nan到最后都是s。确实引发的表达式这样OverflowError做是因为它们从不尝试减去无穷大,因此最终会发现错误。这看起来不像是一个深思熟虑的设计决定。您可以通过更改溢出检查的工作方式来修复它。

于 2013-08-14T23:10:38.143 回答
3

好的,我很确定我想通了。

首先,我注意到一些看起来有些荒谬的事情:

>>> (1e309j)**2
(nan+nanj)
>>> (1e308j)**2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: complex exponentiation

有趣的。也许这根本与复杂的幂运算无关。

>>> (1e309)**2
inf
>>> (1e308)**2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')

更有趣的是。让我们看看 Python 是怎么想的1e3091e308

>>> (1e308)
1e+308
>>> (1e309)
inf

最后,

>>> (1e309)**2
inf
>>> (1e309j)**2
(nan+nanj)

>>> (float('inf') + 1j) ** 2
(nan+nanj)

>>> (1e309j)
infj
>>> (1e309j)**2
(nan+nanj)

任何操作inf都会给我们inf。看起来复数 ( a + bi) 的实现不太倾向于给予inf,而给予更多(nan + nanj)。因此,通常会返回inf而不是返回的东西(nan + nanj)我不确定为什么会这样,也许有人对 Python 有更好的理解inf并且nan可以加入。

简而言之,数字最终会停止溢出,并开始返回inf。计算inf很容易,但数字接近它的计算并不容易!这就是为什么1e309**2起作用而1e308 ** 2不起作用的原因。当与复数配对时,这(无论出于何种原因)给出(nan + nanj). 我只是通过玩控制台才发现这一点的——我希望看到更彻底的解释!

编辑:@user2357112 为此提供了更好的理由。计算复数指数的方式可以包括 的计算inf - inf,它返回nan。我会留下这个来显示模式,但他的回答给出了推理。


作为旁注,我发现这很有趣:

>>> (float('inf') + 1j) ** 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: complex exponentiation
>>> (float('inf') + 1j) 
(inf+1j)
于 2013-08-14T22:51:51.017 回答
-1

Python 整数值可以自动提升为 long 以获得任意精度:

>>> (10**300)**2
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

由于IEEE 浮点的限制,浮点值溢出:

>>> float(10**300)**2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')

Python 复合体的组件是一个 float 类,同样会溢出:

>>> isinstance(complex(1).real,float)
True
>>> isinstance(complex(1).imag,float)
True

取通常的最大双精度值:

>>> max_double=1.7976931348623157e+308

执行大多数在浮点范围内增加该值的步骤,你会得到inf

>>> max_double*10
inf
>>> max_double*max_double
inf
>>> max_double*max_double*max_double*max_double
inf
>>> max_double++10**(308-15)
inf

如果在 FP 窗口之外,尾数和指数不会改变:

>>> md+10**(308-17)
1.7976931348623157e+308
>>> max_double**1.0000000000000001
1.7976931348623157e+308

可以看到溢出:

>>> max_double**1.000000000000001
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')

但正如文件中所述,它的应用不一致:

由于 C 中浮点异常处理缺乏标准化,大多数浮点运算也没有被检查。

可以在这里看到:

>>> (1e300+1e300j)*(1e300+1e300j)
(nan+infj)
>>> (1e300+1e300j)**2
(nan+nanj)
于 2013-08-14T22:42:36.643 回答