I was wondering whether there was an easier way or a shortcut for writing complex(a,b) to get a complex number of the form \(a+bi\) (or \(a+bj\), in keeping with Python’s use of j for \(\sqrt{-1}\). It took me a little while, but the way that I tried to get there may be interesting.

First, I tried to take the square root of -1:

## -1.0

This didn’t work due to a logical but surprising glitch: the operator precedence (i.e., the order in which operators get executed) is higher for the power operator ** than for the unary minus operator -. In other words, Python interprets -1**0.5 as -(1**0.5), not (-1)**0.5.

Then I guessed that there might be a square-root function called sqrt(): this is common in a lot of computer languages

## Traceback (most recent call last):
##   File "<string>", line 1, in <module>
## NameError: name 'sqrt' is not defined

Using a little bit of additional information (I knew/guessed that there is an extra library, or module, in Python called math, and that the following is the way to access it):

import math     ## load the math module
## Traceback (most recent call last):
##   File "<string>", line 2, in <module>
## ValueError: math domain error

OK, that doesn’t work. Apparently math.sqrt defines a function whose domain is restricted to non-negative values (that’s what math domain error means). What if I try reading the help file?


That showed me that there is indeed a pow function for complex numbers (admittedly this is a little hard to read!), but that’s not really the point – I want to take pow() of a negative floating point number with a fractional power.

Just out of curiosity, what happens if I try math.sqrt() on a number that’s already defined as complex?

## Traceback (most recent call last):
##   File "<string>", line 1, in <module>
## NameError: name 'math' is not defined

Oh well, it looks like the math module is really only designed for floating-point operations, not operations on complex numbers …

Coming back to what (in hindsight) I should have done in the first place, I tried (1) the pow() function (which takes care of the order-of-operations problem; pow() is definitely applied after the unary minus sign and (2) making sure I put parentheses around the (-1) when using the ** operator.

## (6.123031769111886e-17+1j)
## (6.123031769111886e-17+1j)

These both work (but only in Python 3: Python 2 gives “negative error cannot be raised to a fractional power”). They don’t give me exactly what I wanted, though. Python tells me the answer is \(\epsilon+i\), where \(\epsilon\) is a tiny number (not quite zero). (This kind of problem is an inevitable consequence of trying to approximate real numbers via finite binary approximations; we will talk about it more in a few weeks.) The R language has a zapsmall function that sets small values to zero; the numpy module for Python has a real_if_close() function, but that’s not quite what we want (we want to set the real part to zero).

If I really wanted to, I could set i (or j) equal to complex(0,1), or to pow(-1,0.5), and then define complex variables via real_part+i*imag_part (e.g. 2.72+0.65*i), but it would only be worth it if I were working with complex variables a lot.

And now, just after I’ve finished with this whole thing, I’ve realized that there was a shortcut all along:

## (2.72+0.65j)

works just fine.

This example illustrates (1) the potential subtlety of order of operations; (2) there’s more than one way to do it; (3) how to experiment/look for help; (4) read/interpret error messages.