我正试图弄清楚这里的模式:
>>> 1e300 ** 2
OverflowError: (34, 'Result too large')
>>> 1e300j ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1j) ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1e300j) ** 2
(nan+nanj)
这种行为似乎不仅在理论上没有说明,而且在实践中也很奇怪!是什么解释了这个?
答案 0 :(得分:6)
查看复数取幂的source表明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
只会设置nan
,所以它会错过溢出并继续前进。
总之,Python只检查溢出的最终结果,而不检查中间值,这会导致它错过中间的溢出,因为它最后都是OverflowError
。引发{{1}}的表达式会这样做,因为它们从不尝试减去无穷大,因此最终会发现错误。它看起来不像是一个刻意的设计决定;你可以通过改变溢出检查的工作方式来修复它。
答案 1 :(得分:2)
好的,我很确定我想出来了。
首先,我注意到一些看起来有些荒谬的事情:
>>> (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对1e309
和1e308
的看法:
>>> (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
和{{{ 1}}可以跳进去。
简而言之,数字最终会停止溢出,并开始返回nan
。使用inf
进行计算很简单,但数字附近的计算不是很好!这就是为什么inf
在1e309**2
不起作用的原因。当与复数匹配时,这(无论出于何种原因)给出1e308 ** 2
。我只是通过玩控制台才发现这一点 - 我很乐意看到更彻底的解释!
编辑:@ user2357112给出了更好的理由。计算复指数的方式可以包括(nan + nanj)
的计算,其返回inf - inf
。我会把它留下来显示模式,但他的答案给出了推理。
作为旁注,我发现这很有趣:
nan
答案 2 :(得分:-1)
Python整数值可以自动推广到任意精度的长度:
>>> (10**300)**2
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
的限制,浮点值会溢出
>>> 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
采用通常的最大double precision值:
>>> 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窗口之外,则mantisa和exponent不会改变:
>>> 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')
但是作为stated in the documents,它的应用不一致:
由于缺乏浮点异常的标准化 在C中处理时,大多数浮点运算也不会被检查。
在这里可以看到:
>>> (1e300+1e300j)*(1e300+1e300j)
(nan+infj)
>>> (1e300+1e300j)**2
(nan+nanj)