You are on page 1of 13

See

discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/267386724

Notes on Complex Numbers for Computer


Scientists

Article

CITATIONS READS

0 1,633

2 authors:

Ken Hawick Daniel P Playne


University of Hull Massey University
358 PUBLICATIONS 2,446 CITATIONS 49 PUBLICATIONS 394 CITATIONS

SEE PROFILE SEE PROFILE

Some of the authors of this publication are also working on these related projects:

Human Health and Emerging Environmental Contaminants View project

All content following this page was uploaded by Ken Hawick on 27 January 2015.

The user has requested enhancement of the downloaded file.


Technical Report CSTN-048

Notes on Complex Numbers for Computer Scientists


K.A. Hawick and D.P. Playne
Institute of Information and Mathematical Sciences
Massey University Albany
North Shore 102-904, Auckland, New Zealand
Email: k.a.hawick@massey.ac.nz and daniel.playne@gmail.com
Tel: +64 9 414 0800 Fax: +64 9 441 8181

July 2008

Abstract
Complex numbers are a useful piece of mathematical apparatus that play a critical role in understand-
ing many applications such as quantum computation. This note summarises some key properties as well
as some systems and packages that support programming with complex numbers. We discuss complex
number support in Fortran, Python and D as well as library and package support in other languages
such as C/C++, Java and Ruby.

Keywords: complex numbers; numerical precision; Java; C++; D; FORTRAN; Ruby; Python

1 Complex Numbers
Complex numbers consist of a pair of ordinary Real numbers. Sometimes written as c = (a, b) or z = (x, y) :
x, y R, where a or x is the real part - written Re c or Re z and b or y is the imaginary part, written
Im c or Im z. The use of the word imaginary is historical and perhaps misleading. There is nothing
mystical or weird about complex numbers and the so-called imaginary part is just an ordinary real number
but with a specific contextual meaning. The two ordinary numbers that make up the ordered pair that is a
complex number are often used to denote some entity that has a magnitude and a phase or angle. This is
shown in figure 1.
The pair of numbers specifies a vector in two-dimensional space and is shown drawn on the conventional
Cartesian x-y axes. We can think of a complex number as having its real part represented horizontally on the
conventional real number line taking vales , + and the imaginary part represented vertically and also
taking on , + values. We can also represent the complex number vector using a length (or magnitude)
drawn as r and an angle - drawn as . We often use lower case Greek letters such as theta or phi for
angles.
Pythagoras tells us what the magnitude or length r of the vector is and elementary trigonometry tells us the

1
y

b c = (a,b)

r
angle

x
a

Figure 1: A Complex number c = (a, b) drawn as a vector on conventional Cartesian x-y axes.

angle :

a = Re c (1)
b = Im c (2)
p
r = |c| = abs c = (a2 + b2 ) (3)
a = r cos (4)
b = r sin (5)
b
= arg c = arctan (6)
a

We can use the notion of the square root of 1 denoted as i : i2 1 (or sometimes j in engineering texts)
to exploit the algebra of complex numbers, that will let us use them more effectively.
We represent c = a + ib and use various algebraic properties including:

c = a + ib = rei = r (cos + i sin ) (7)

We need to define +, , operations for complex numbers. Sensible definitions all arise from the master
equation 7:

2
c3 = c1 c2 = (a1 + ib1 )(a2 + ib2 ) = (a1 a2 +i2 b1 b2 ) + i(a1 b2 + b1 a2 )
= (a1 a2 b1 b2 ) + i(a1 b2 + b1 a2 ) = a3 + ib3 (8)
c4 = c1 + c2 = a1 + ib1 + a2 + ib2 = (a1 + a2 ) + i(b1 + b2 ) = a4 + ib4 (9)

It is useful to define the complex conjugation operation that changes the sign of the imaginary part so that
c = a + ib : c a ib = rei . This implies various other useful relations:

1
Re c = (c + c ) = a (10)
2
1
Im c = (c c ) = b (11)
2i
2
|c| = cc = norm c (12)
2iarg c c
e = e2i = (13)
c

(c1 c1 ) = c1 c2 (14)
|c1 c2 | = |c1 | . |c2 | (15)
|c1 + c2 | <= |c1 | + |c2 | (16)

We can define an inverse (to multiplication) c1 so that:


c c a ib
c1 =
= 2 = 2 (17)
cc |c| a + b2

Using the r, form, note also that:

r1 ei1 .r2 ei2 = r1 r2 ei(1 +2 ) (18)


n
rei = rn ein (19)
1 1
rei n = r n ei n (20)

The angle is periodic - a complete rotation of 360 degrees or 2 radians leaves the trigonometrical functions
unchanged, so that:

ei = ei(+2) = ei(+4) = ei(+6) = . . . (21)


1 1 1 +2
rei n = r n ei n = r n ei n = . . . (22)

and so every complex number has n n-th roots:

1  n1 1 +2k
n
c = c n = rei = r n ei n , k = 0, 1, 2, . . . , n (23)

2 Programming with Complex Numbers


Various programming languages and systems support complex numbers directly but in some languages a
separate package or module must be used to enable their use.
In summary:

3
Language Mechanism Data type
C/C++ include <complex.h> complex t
Fortran built-in COMPLEX
D built-in cdouble
Python built-in complex
Ruby require complex complex

Table 1: Support for Complex Number type in Different Programming Languages

3 Programming with Complex Numbers in C++


In C++ the package complex.h provides a mechanism and library functions based on template classes.
We have:

a template class complex of float, double and long double


real, imag returning a real
functions: abs, arg and norm returning a real
functions: conj and polar(r, theta) returning a complex
cos, cosh, sin, sinh, tan, tanh, exp, log, log10, pow, sqrt
the usual operators : +, , , / are all defined as well as the C style operators: + =, =, ... etc.
C++ stream insertion and extraction operators.

Use of these functions is illustrated in the C++ code segment below. The package defines a template class
for complex - it is convenient in this present age of 64 bit processor architectures to decide to use complex
numbers that consist of a pair of doubles. A double is likely to be a 64 bit floating point number on many
systems, although note that the language standard does not mandate this. It is convenient to use the modern
name convention for a type definition and define the data type complex t accordingly.
The code segment in figure 2 will produce output like:

(1,0) = 1 + i. 0
(2,-2) = 2 + i. -2
(2,-2) = 2 + i. -2 = 2.82843 e^ i -0.785398
(0.25,0.25) = 0.25 + i. 0.25 = 0.353553 e^ i 0.785398

Note that 0.785398 corresponds to 45 degrees, and that 2.82843 is approximately 2 2.

4 Complex numbers in JavaT M


The JavaT M programming language does not have the in-built capability to work with complex numbers.
To solve this problem we must define our own class (Complex) which will allow us to to perform complex
arithmetic. The class Complex stores the real and imaginary values of the complex number and provides
a number of methods to allow us to work with these Complex objects. The methods (similar to the C++
methods) that should be provided are:

4
#include <i o s t r e a m >
#include <complex>

using namespace s t d ;

typedef complex<double> c o m p l e x t ;

i n t main ( i n t argc , char argv [ ] ) {

complex t c1 , c2 , c3 , c4 ;

double a1 , a2 , a3 , a4 , b1 , b2 , b3 , b4 ;
double r3 , r4 , t h e t a 3 , t h e t a 4 ;

a1 = 1 . 0 ;
b1 = 0 . 0 ;

a2 = 2 . 0 ;
b2 = 2.0;

c1 = c o m p l e x t ( a1 , b1 ) ;
c2 = c o m p l e x t ( a2 , b2 ) ;

c o u t << c1 << = << a1 << +i . << b1 << e n d l ;


c o u t << c2 << = << a2 << +i . << b2 << e n d l ;

c3 = c1 c2 ;

a3 = r e a l ( c3 ) ;
b3 = imag ( c3 ) ;

r 3 = abs ( c3 ) ;
t h e t a 3 = a r g ( c3 ) ;

c o u t << c3 << = << a3 << +i . << b3 << =


<< r 3 << e i << t h e t a 3 << e n d l ;

c4 = c1 / c2 ;

a4 = r e a l ( c4 ) ;
b4 = imag ( c4 ) ;

r 4 = abs ( c4 ) ;
t h e t a 4 = a r g ( c4 ) ;

c o u t << c4 << = << a4 << +i . << b4 << = << r 4 << e i << t h e t a 4 << e n d l ;

exit (1);
}

Figure 2: C++ Code Example showing use of typical complex t data type from complex.h.

5
re, im fields, accessing a real (double)
functions: abs phase returning a real
functions: conjugate reciprocal returning a Complex
functions: log exp sqrt returning a Complex
functions: cos sin tan acos asin atan returning a Complex
functions: cosh sinh tanh acosh asinh atanh returning a Complex
functions: plus minus times divides returning a Complex

These functions will allow us to perform arithmetic and use Complex numbers in our calculations. The
basic arithmetic +-*/ for our Complex class must be performed by methods such as plus(Complex) and
minus(Complex) due to the fact that JavaT M does not provide us with any way to overload operators. The
signature for our Complex class is shown in figure 3.

public c l a s s Complex {
private double r e ;
private double im ;

public Complex ( double r e a l , double imag ) ;


public S t r i n g t o S t r i n g ( ) ;

public double r e ( ) ;
public double im ( ) ;

public double abs ( ) ;


public double phase ( ) ;

public Complex p l u s ( Complex number ) ;


public Complex minus ( Complex number ) ;
...
}

Figure 3: Java Interface specification for a complex class.

One interesting dilemma this style causes is the order in which the expressions are evaluated. c1.plus(c2) quite
clearly represents c1+c2 and c1.minus(c2) should mean c1c2. The order of multiplication is not important
however it is vital for division; it is not entirely clear whether c1.divides(c2) should represent c1 c2
c2 or c1 . For
this reason it was decided that it a better naming convention would be to have the methods times(Complex)
and over(Complex). These names leave no ambiguity in the meaning of equations. c1.over(c2) quite clearly
represents c1
c2 .

Since JavaT M lacks the operator overloading features that are available in C++ we end up with a complex
arithmetic capability that is much more complicated to construct and less directly related to the normal
mathematics syntax than we would be able to attain with a truly built in type. For example the relatively
simple equation c1 = c2/c3 + c4/c5 must be written in the form c1 = c2.over(c3).plus(c4.over(c5)). This
can make writing more complex equation extremely tricky and error prone, however there is a possible
alternative. Although JavaT M does not support operator overloading there is a preprocessor available called
JFront [1] which can provide similar functionality. JFront processes the JavaT M code before it is compiled
into JavaT M byte code and replaces defined operators with standard JavaT M code. For example, the +
operator can be defined by:

6
public c l a s s Complex {
...
Complex o p e r a t o r +(Complex c2 ) {
Complex r e s u l t = new Complex ( ) ;
r e s u l t . r e = t h i s . r e + c2 . r e ;
r e s u l t . im = t h i s . im + c2 . im ;
return r e s u l t ;
}
...
}

This operator can then be used in the form:


c1 = c2 + c3
Before the code is sent to JavaT M , JFront will analyse the code to find the operators defined in each class.
After this step JFront will have changed the Complex class code into:
public c l a s s Complex {
...
Complex o p e r a t o r p l u s ( Complex c2 ) {
Complex r e s u l t = new Complex ( ) ;
r e s u l t . r e = t h i s . r e + c2 . r e ;
r e s u l t . im = t h i s . im + c2 . im ;
return r e s u l t ;
}
...
}

After the class operators have been detected and changed, it will look for the usage of these operators within
the rest of the program and replace them with the appropriate method calls. The example equation will be
replaced by:
c1 = c2.operatorplus(c3)
This effectively automates the process of converting the equation in mathematical notation into the JavaT M
code. JFront provides a simple way of easily expressing Complex arithmetic in the JavaT M programming
language.

5 Complex Numbers in Fortran


Many scientists may have first used complex numbers in programs using Fortran [2]. Fortran has supported
a built in COMPLEX data type consisting of a pair of single precision floating point numbers since its early
language standards. No special compiler directives or include packages are required to use complex numbers
and perform complex arithmetic within Fortran.
Along with support for basic complex number arithmetic, Fortran also supports built in generic functions
for ABS, EXP, LOG, SQRT, SIN, COS, TAN and so forth. The precision of the Fortran complex numbers can be
improved within most modern Fortran compilers by the use of a mechanism such as KIND to specify different
precisions and it is possible to have a double precision (64 bit) complex number type. While there is much to
be said for the orthogonality of COMPLEX with the other types in Fortrans, it is likely that newer programming
projects will use languages like D and its complex number facilities.

7
PROGRAM MYPROG
COMPLEX A, B, C
A = COMPLX( 1 2 3 4 . 0 , 5 6 7 8 . 0 )
READ( 5 , ) B
C = A B

WRITE( 6 , 1 0 0 0 ) C
1000 FORMAT( 1H0 , C IS : , F9 . 4 , + i , F9 . 4 )

C Make C e q u a l to i t s complex c o n j u g a t e :
C = CONJG(C)

WRITE( 6 , ) Imaginary p a r t o f C i s : , AIMAG(C)


WRITE( 6 , ) Real part of C i s : , REAL(C)

STOP
END

Figure 4: Uses of the COMPLEX data type in Fortran

6 Complex Numbers in D
The D programming language [3] supports complex numbers as full inbuilt language types. The cdouble
type implements a a complex number as a pair of 64-bit doubles - one each for the real and imaginary parts.
The D language also supports real and creal that are the native machine floating point types. On a MacPro
workstation using Intel Xeon processors the real corresponds to an 80 bit floating point representation which
can be exploited for extra precision, although the IEEE portable 64 bit double and cdouble may be the
normal programmer choices.
The code in figure 5 produces the following output using creal:

2+1i
true
2
2
z 1.41421356237309505+1.41421356237309505i
abs 2
conj 1.41421356237309505+-1.41421356237309505i
norm 4
2
1.38629436111989062
54.5981500331442391

and the (different) output using cdouble:

2+1i
true
2
2
z 1.41421356237309515+1.41421356237309515i
abs 2.00000000000000014

8
import s t d . s t d i o ;
import s t d . math ;

r e a l norm ( c r e a l z ) {
return z . r e z . r e + z . im z . im ;
}

void main ( char [ ] [ ] args ){

c r e a l z1 , z2 ;
i r e a l i1 = 2.0 i ;

z1 = 2 . 0 + 1 . 0 i ;

w r i t e f l n ( %s , z1 ) ;

z1 = z1 . r e + 1 . 5 i ;
z1 = z1 . r e + i 1 ;

z2 = z1 . r e + z1 . im 1 i ;
w r i t e f l n ( %s , z1 == z2 ) ;

w r i t e f l n ( %s , z1 . r e ) ;
w r i t e f l n ( %s , z1 . im ) ;

z1 /= 2 . 0 ;
z1 = 2 . 0 ;
z1 = s q r t ( z1 . r e ) + s q r t ( z1 . im ) 1 i ;

writefln (z %.18 s , z1 );

w r i t e f l n ( abs %.18 s , abs ( z1 ) ) ;


w r i t e f l n ( c o n j %.18 s , c o n j ( z1 ) ) ;
w r i t e f l n ( norm %.18 s , norm ( z1 ) ) ;

w r i t e f l n ( %.18 s , s q r t ( norm ( z1 ) ) ) ;
w r i t e f l n ( %.18 s , l o g ( norm ( z1 ) ) ) ;
w r i t e f l n ( %.18 s , exp ( norm ( z1 ) ) ) ;
}

Figure 5: A complete D program showing use of creal. Substituting cdouble for creal employes a pair of
64 bit IEEE floating point numbers.

9
conj 1.41421356237309515+-1.41421356237309515i
norm 4.00000000000000089
2.00000000000000022
1.38629436111989084
54.5981500331442876

Some precision is perceptably lost during the calculations using 64 bits. Note in particular that to 18 decimal
places the abs is no longer exact using 64 bits.
D is a relatively recent programming language and current implementations may in fact use macros and other
apparatus hidden from the user to implement the D language on top of a C or C++ compiler. Consequently
the complex package may in fact be implemented as some overloaded operators that invoke the library
routines or other macros from a C/C++ complex.h package.

7 Precision and Numerical Issues


Although the definition of the complex modulus is:
p
|a + ib| = a2 + b2 (24)

This is not a numerically safe way to compute it as this can result in drastic error if either the real or
imaginary parts are comparable with the square root of the largest representable number. A safer way to
perform this calculation is:
( p
|a| 1 + (b/a)2 , |a| |b|
|a + ib| = p (25)
|b| 1 + (a/b)2 , |a| |b|

A similar issue arises with complex division and a numerically safe way to avoid potential overflow is to use
(the somewhat cumbersome) formulation:
(a+b(d/c))+i(ba(d/c))

c+d(d/c) , |c| |d|




a + ib (a+b(d/c))+i(ba(d/c))
= c+d(d/c) , |c| |d| (26)
c + id



(a(c/d)+b)+i(b(c/d)a)

c(c/d)+d , |c| |d|

Complex square roots need also be approached carefully with a multi-branch formula:


0, w=0
d

w + i , 6 0, c 0
w=



2w



c + id = |d| (27)


2w + iw, w 6= 0, c 0, d 0




|d|

2w iw, w 6= 0, c 0, d 0

10
where:

0, c=d=0






r
q
1+ 1+(d/c)2

w= |c| |c| |d|
2 , (28)





r

q
|d| |c/d|+ 1+(c/d)2 , |c| |d|


2

Unfortunately these safe but slower algorithms are often not implemented in complex.h packages and
therefore C++ and potentially D implementations that are built upon them are not necesarily safe against
overflows. It is not; however, difficult to adapt the necessary overloaded operators as defined in the system
include files for open compiler implementations.
More information on the effect of overflow in complex numbers is available in [4, 5].

8 Complex Numbers in Python and Ruby


Very modern interpreted environments such as the interpreted language Python [6] now typically come with
built in support for complex arithmetic. In Python the numeric literal j is used as a suffix to denote an
imaginary number. Figure 6 is a fragment of Python code illustrating the use of complex numbers.

#! / u s r / l o c a l / b i n / p y t h o n

print 1 + 2j

print 1 . 0 complex ( 1 . 0 , 2.0 )

a = 1234.0 + 5678.0 j

print a . r e a l

print a . imag

Figure 6: A short Python program showing the built in support for complex numbers using the numeric
literal j.

The Python code in figure 6 produces the following output:

(1+2j)
(1-2j)
1234.0
5678.0

Other scripting languages such as Ruby [7] require a library package to support complex arithmetic, but do
in fact have a richer set of associated functions and operators.

11
9 Summary
In summary, complex numbers are readily programmed in several modern programming languages although
users must sometimes still give care and attention to precision and overflow issues, depending upon the needs
of applications. Many systems will either support complex arithmetic using built in types or will simply
require inclusion of a standard supplied library or package to enable them.

References
[1] Garnatz and Grovender Inc: Jfront (2008)

[2] Metcalf, M., Reid, J., Cohen, M.: Fortran 95/2003 Explained. Oxford University Press (2004) ISBN
0-19-852693-8.

[3] Bright, W.: D Programming Language. Digital Mars. (2008)

[4] Knuth, D.: The Art of Computer Programming: Seminumerical Algorithms. 3rd edn. Volume 2. Addison-
Wesley (1997)

[5] Midy, P., Yakovlev, Y.: Computing some elementary functions of a complex variable. Mathematics and
Computers in Simulation 33 (1991) 3349

[6] van Rossum, G.: Python essays. http://www.python.org/doc/essays/ (2008)

[7] Thomas, D., Fowler, C., Hunt, A.: Programming Ruby: The Pragmatic Programmers Guide. 3rd edn.
Pragmatic Programmers (2004) ISBN 978-0-9745140-5-5.

12

View publication stats