Skip to content Skip to sidebar Skip to footer

In What Cases Does Python Complex Exponentiation Throw An Overflowerror?

I’m trying to figure out the pattern here: >>> 1e300 ** 2 OverflowError: (34, 'Result too large') >>> 1e300j ** 2 OverflowError: complex exponentiation >>

Solution 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 nans 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.

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

Solution 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 calllast):
  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 calllast):
  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)

Post a Comment for "In What Cases Does Python Complex Exponentiation Throw An Overflowerror?"