在什么情况下,Python复杂求幂会引发溢出错误?

在什么情况下,Python复杂求幂会引发溢出错误?,python,floating-point,complex-numbers,Python,Floating Point,Complex Numbers,我在这里试图找出模式: >>> 1e300 ** 2 OverflowError: (34, 'Result too large') >>> 1e300j ** 2 OverflowError: complex exponentiation >>> (1e300 + 1j) ** 2 OverflowError: complex exponentiation >>> (1e300 + 1e300j) ** 2 (nan+na

我在这里试图找出模式:

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

这种行为不仅在理论上没有具体说明,在实践中也很奇怪!这是怎么解释的?

Python整数值可以自动转换为任意精度的long:

>>> (10**300)**2
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
由于以下限制,浮点值溢出:

取通常的最大值:

执行在浮点范围内增加该值的大多数步骤,您将得到
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窗口之外,mantisa和指数不变:

>>> 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')

好吧,我很确定我找到了答案

首先,我注意到一些似乎有些可笑的事情:

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

>>> (1e309)**2
inf
>>> (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)
>(浮点('inf')+1j)**1
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
溢出错误:复数求幂
>>>(浮动('inf')+1j)
(inf+1j)
查看复杂的求幂运算,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
a
b
非常大时,这将变成浮点无穷大减去浮点无穷大,即
nan
nan
结果传播,经过几次操作后,结果是
(nan+nanj)
Py_ADJUST_ERANGE2
仅在看到无穷大时设置
errno
,因此它忽略溢出并继续前进


总之,Python只检查溢出的最终结果,而不是中间值,这会导致它错过中间的溢出,因为它最终都是“代码> n< /代码> s。确实引发溢出错误的表达式之所以这样做,是因为它们从不尝试减去无穷大,因此错误会在末尾被发现。这看起来不像是一个深思熟虑的设计决策;您可以通过更改溢出检查的工作方式来修复它。

此外,
(1e300+1e300j)**100=>(nan+nanj);(1e300+1e300j)**101==>Traceback(最近一次调用last):Overflower中的文件“”,第1行错误:复数求幂
1e300是一个浮点值,因此它不会自动执行。这是通过以下方式实现的:
(1*10**300)**2
杰森:如果您有时间打开一个问题,我们将不胜感激。@MarkDickinson:正确的结果是什么?什么是bug?@drewk:不确定我是否会称之为bug(例如,在Python 2.7中修复这个问题可能没有意义),但有时引发异常,有时返回NaN和无穷大的不一致性令人不安。理想情况下,它应该在所有情况下都执行一个或另一个(我个人会选择例外)。在Python3.4中解决这个问题会很好。这并不能解决这个问题。为什么
(1e300+1e300j)**2
不引发
溢出错误
?因为中间值溢出。试试看
1.7976931348623157e+308*1.0000000000001
@drewk我否决了这一点,因为它似乎与问题完全无关。现在我看到了它的相关性,我想改变我的投票,但我不能,除非你编辑答案!你的电话,这有什么关系?我们知道浮子会溢出。我们知道复数由两个浮点数表示。当浮点求幂中发生溢出时,Python应该引发一个
溢出错误。问题是为什么
(1e300+1e300j)**2
没有引发预期的
溢出错误。这不是问题所在。我已经在回答中发布了完整的分析。@user2357112实际上,你的解释与我几乎所有的回答都是一致的——你只是更具体地解释了为什么我们会得到
(nan-nanj)
-1:这是一个非常好的回答,但是语句
确实引起溢出错误的表达式之所以这样做,是因为它们从不尝试减去无穷大,所以错误在结尾就被发现了。
是完全错误的。@thewolf:真的吗?问题是什么?我怀疑@thewolf将“引起
溢出错误的表达式”
”解释为指关于Python操作的一般情况或一般陈述,而不是这个问题中的具体示例。啊。不,我只是说问题中的那些。
>>> (1e309)**2
inf
>>> (1e308)**2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
>>> (1e308)
1e+308
>>> (1e309)
inf
>>> (1e309)**2
inf
>>> (1e309j)**2
(nan+nanj)
>>> (float('inf') + 1j) ** 2
(nan+nanj)

>>> (1e309j)
infj
>>> (1e309j)**2
(nan+nanj)
>>> (float('inf') + 1j) ** 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: complex exponentiation
>>> (float('inf') + 1j) 
(inf+1j)
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