问题
I’m trying to figure out the pattern here:
>>> 1e300 ** 2
OverflowError: (34, 'Result too large')
>>> 1e300j ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1j) ** 2
OverflowError: complex exponentiation
>>> (1e300 + 1e300j) ** 2
(nan+nanj)
The behavior seems to be not only unspecified in theory, but also weird in practice! What explains this?
回答1:
A look at the source for complex exponentiation shows that Python only checks for overflow at the end of the computation. Also, there's a special case for small integer exponents that uses exponentiation by squaring, which involves complex multiplication.
r.real = a.real*b.real - a.imag*b.imag;
r.imag = a.real*b.imag + a.imag*b.real;
This is the formula for complex multiplication. Note the following:
a.real*b.real - a.imag*b.imag
When a
and b
are very large, this becomes floating-point infinity minus floating-point infinity, which is nan
. The nan
results propagate, and after a few operations, the result is (nan+nanj)
. Py_ADJUST_ERANGE2
only sets errno
if it sees an infinity, so it misses the overflow and goes on its way.
In summary, Python only checks the end result for overflow, not intermediate values, and this causes it to miss the overflow in the middle because it's all nan
s by the end. The expressions that do raise OverflowError
do so because they never try to subtract infinities, so the error is spotted by the end. It doesn't look like a deliberate design decision; you could fix it by changing the way the overflow check works.
回答2:
Ok, I'm pretty sure I figured this out.
First, I noticed something that seemed somewhat ridiculous:
>>> (1e309j)**2
(nan+nanj)
>>> (1e308j)**2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: complex exponentiation
Interesting. Maybe this doesn't have to do with complex exponentiation at all.
>>> (1e309)**2
inf
>>> (1e308)**2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
Even more interesting. Let's see what Python thinks of 1e309
and 1e308
:
>>> (1e308)
1e+308
>>> (1e309)
inf
Finally,
>>> (1e309)**2
inf
>>> (1e309j)**2
(nan+nanj)
And
>>> (float('inf') + 1j) ** 2
(nan+nanj)
>>> (1e309j)
infj
>>> (1e309j)**2
(nan+nanj)
Any manipulations of inf
gives us inf
. It looks like the implementation of complex numbers (a + bi
) are less inclined to give inf
, and more to give (nan + nanj)
. So, something that would normally return inf
instead returns (nan + nanj)
I'm not sure why this is, maybe someone with a better understanding of Python's inf
and nan
could jump in.
In short, numbers stop overflowing eventually, and start returning inf
. Calculations with inf
are easy, but calculations with numbers close to it aren't! That's why 1e309**2
works while 1e308 ** 2
doesn't. When paired with complex numbers, this (for whatever reason) gives (nan + nanj)
. I only found this out by playing with the console -- I'd love to see a more thorough explanation!
Edit: @user2357112 gives a much better reason for this. The way complex exponents are calculated can include a calculation of inf - inf
, which returns nan
. I'll leave this up to display the pattern, but his answer gives the reasoning.
As a side-note, I found this funny:
>>> (float('inf') + 1j) ** 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: complex exponentiation
>>> (float('inf') + 1j)
(inf+1j)
回答3:
Python integer values can autopromote to a long for arbitrary precision:
>>> (10**300)**2
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Float values overflow because of the limitation of the IEEE floating point:
>>> float(10**300)**2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
The component of a Python complex is a class of float, subject to the same overflow:
>>> isinstance(complex(1).real,float)
True
>>> isinstance(complex(1).imag,float)
True
Take the usual max double precision value:
>>> max_double=1.7976931348623157e+308
Do most steps that increase that value in the range of the floating point range and you get 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
If outside of the FP window, the mantisa and exponent do not change:
>>> md+10**(308-17)
1.7976931348623157e+308
>>> max_double**1.0000000000000001
1.7976931348623157e+308
Overflow can be seen:
>>> max_double**1.000000000000001
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: (34, 'Result too large')
But as stated in the documents, it is inconsistently applied:
Because of the lack of standardization of floating point exception handling in C, most floating point operations also aren’t checked.
Which can be seen here:
>>> (1e300+1e300j)*(1e300+1e300j)
(nan+infj)
>>> (1e300+1e300j)**2
(nan+nanj)
来源:https://stackoverflow.com/questions/18243270/in-what-cases-does-python-complex-exponentiation-throw-an-overflowerror