Complex number library

Version of June, 2005

Introduction

This is a rather more extensive complex number library than the one provided in the standard library. It provides complex, imaginary and polar classes and a representation of the i or j of complex analysis. It does not use templates. As in my other libraries, this library is based on a floating point type Real. This must be typedefed to be doublefloat or a class defined by the user (this is done in the file include.h).

This library is not going to replace the one in the standard library - so use this one only if you need its additional facilities or you really don't want to use the standard one.

As usual with my libraries, there are no restrictions on the use of this library. If you distribute it, you should note that it is available essentially for free on the Internet. 

Use is at your own risk. I take no responsibility for errors and problems or losses you may incur through the use of this library. But please report errors.

See the documentation for newmat11 for setting options in the file include.h .

Go to the download page for the library.

Author and site details

Author: Robert Davies; robert at statsresearch.co.nz.

Available from http://www.robertnz.net

Files in this package

The following files are included in this package

cx.h header file for the complex number library
cx.cpp function bodies
cx_polar.cpp function bodies for polar class
boolean.h simulation of the standard Boolean type
myexcept.h header for the exceptions simulator
myexcept.cpp bodies for the exceptions simulator
include.h options header file (see documentation in newmat10)
cxtest.h test program header file
cxtest.cpp test program main function
cxtest1.cpp test unary functions
cxtest2.cpp test binary functions 
cxtest.txt output from the test program
array1.h simple array class used by test program
mandel.cpp Mandelbrot set example
mandel.txt Output from mandel.cpp
cx_targ.txt target file for use with genmake
cx_b55.mak make file for Borland 5.5 compiler
cx_b56.mak make file for Borland 5.6 compiler
cx_m6.mak make file for VC++ 6, 7 compilers
cx_ow.mak make file for Open Watcom compiler
cx_gnu.mak make file for Gnu compiler
cx_cc.mak make file for CC compiler
cx_i8.mak make file for Intel 8.1 compiler for Windows
cx_il8.mak make file for Intel 8.1 compiler for Linux
cx.htm this file
rbd.css Style sheet for cx.htm
polar.gif diagram of polar coordinates
mandel.gif Mandelbrot set example

Testing and getting started

I have tested this program on the Borland 5.5,5.6; MS VC++ 6,7; Gnu 3.3, 3.4; Intel 8. The test program uses templates so you will not be able to run the test program on compilers that don't support templates.  I couldn't  get the templates in the test program to compile with the Sun CC compiler. The program didn't compile with the Open Watcom (1.3) compiler.

I include make files for the test programs for several compilers - see the file list. Make files for some other compilers can be generated with my genmake utility.

If the program is working correctly the output from the test program will end with two lines similar to

   maximum from cxtest1: 3.10082e-14
   maximum from cxtest2: 7.10543e-15
The exact values will, most likely, differ. These values are from the Borland 5.02 compiler.

For older compilers that don't support bool variables you need to edit include.h to enable my simulated Booleans (comment out the statement #define bool_LIB 0).

You will need to #include files include.h and cx.h in your programs that use this package. Edit include.h to determine whether exceptions are to be used, simulated or disabled. If you use the simulated exceptions you should turn off the exception capability of a compiler that does support exceptions. If you want to use namespace activate the use_namespace definition. This library uses the namespace name RBD_COMPLEX.

Change history

May, 2000 Initial release
July, 2001 Improve compatibility with my other libraries, include sum_square() function

Classes

class description user
class CX complex yes
class Imag imaginary yes
class ImaginaryUnit the i or j of complex analysis yes
class Polar polar yes
class ConvertFromReal prevent implicit conversion no
class Quadrant mod 4 arithmetic no

Class CX is the main complex class. I have chosen the name CX so as not to conflict with other complex classes. For example, using complex will clash with the standard library.

Class Imag defines imaginary variables. If you have a complex variable that will always be imaginary (the real part is always zero), then use the Imag class to save computer time and make the program clearer.

Class ImaginaryUnit is used to represent i = sqrt(-1). I use this to define _I_ as a global variable to represent i.

Class Polar represents a complex number in polar coordinates.

I use class ConvertFromReal is used to prevent implicit conversions from Real to Imag. Modern C++ compilers use the keyword explicit to prevent implicit conversion, but, at present I don't want to assume all compilers implement explicit.

Class Quadrant is used by class Polar in the representation of the angle part of the polar coordinate.

The angle in the polar coordinates is represented by theta which is no greater than pi/4 in absolute value and represents the angle from one of the axes. Quadrant shows which axis. A char value in a Quadrant object can take on one of the values 0, 1, 2 or 3 corresponding to the positive real axis, the positive imaginary axis, the negative real axis or the negative imaginary axis respectively. This representation means that one doesn't lose accuracy when converting from complex coordinates to polar coordinates for complex numbers very close to the real or imaginary axes.

Polar coordinate diagram

Constructors

Complex

CX() constructor, don't set values
CX(Real x, Real y) set real part to x, imaginary part to y
CX(Real x) set real part to x, imaginary part to 0
CX(Imag y) set real part to 0, imaginary part to y
CX(ImaginaryUnit) set real part to 0, imaginary part to 1
CX(const CX& z) copy constructor
CX(const Polar& p) convert polar value p to complex

Imaginary

Imag() constructor, don't set values
Imag(ConvertFromReal x) set real part to x. ConvertFromReal stops implicit conversions
Imag(const Imag& z) copy constructor
Imag(ImaginaryUnit) set value to i

Polar

Polar() constructor, don't set values
Polar(Real r, Real theta) set polar coordinates to r, theta after normalising
Polar(Real x) set polar coordinates to x, 0 or -x, pi
Polar(Imag y) set polar coordinates to y, pi/2 or -y,-pi/2
Polar(ImaginaryUnit) set polar coordinates to 1, pi/2
Polar(const CX& z) convert complex z to polar 
Polar(const Polar& p) copy constructor

Member functions

Function Description Return types with this class
    CX Imag _I_ Polar
real() const real part Real Real   Real
real() ref. to real part Real&      
imag() const imaginary part Real Real   Real
imag() ref. to imaginary part Real& Real&    
imag_as_imag() const imaginary part as Imag Imag Imag   Imag
conj() const conjugate CX Imag   Polar
cabs() const absolute value Real Real   Real
cabs() ref. to absolute value       Real&
arg() const argument Real Real   Real
sum_square() const square of cabs Real Real   Real
operator+() const + prefix op. CX Imag _I_ Polar
operator-() const - prefix op. CX Imag Imag Polar
theta() const theta       Real
theta() ref. to theta       Real&
quadrant() const quadrant       Quadrant
quadrant() ref. to quadrant       Quadrant&
assert_is_valid() const
AssertIsValid() const
check Polar valid       void

For example:

   CX z = 3.0 + 4.0 * _I_;
   cout << z.real() << ", " << z.imag() << endl;

outputs 3.0, 4.0 .

The declaration of z could have been replaced with any of the following:

   CX z(3.0, 4.0);
   CX z; z.real() = 3.0; z.imag() = 4.0;
   CX z = 3.0 + Imag(4.0);
   Polar p(5.0, atan(4.0 / 3.0)); CX z = p;

Note that there are two versions of z.real() and z.imag() for CX variable, z. The non-const version is used when z hasn't been declared as const. This version can be used on the left-hand side of an = sign. The same applies for p.cabs(), p.theta() and p.quadrant() for Polar variable p.

It might seem more natural for z.imag() to return a variable of type Imag rather than Real. However, this would be unusual for those used to the standard library or Fortran. I include another function z.imag_as_imag() which does return the imaginary part as type Imag. Nevertheless, there is a potential source of error, here, if z.imag() is used when z.imag_as_imag() was intended.

sum_square() is the norm function in the standard. It isn't a norm in the mathematical sense so you wouldn't call it norm would you?

assert_is_valid checks that a Polar variable is valid. An exception is thrown if the value is invalid; for example the absolute value of the member variable Theta is greater than pi/4. This should not be possible unless a Polar object is uninitialised or invalid values have been entered with the cabs, theta or quadrant functions.

Global functions

Function Description Return types with this class
    CX Imag _I_ Polar
real(.) real part Real Real   Real
imag(.) imaginary part Real Real   Real
imag_as_imag(.) imaginary part as Imag Imag Imag   Imag
conj(.) conjugate CX Imag   Polar
cabs(.) absolute value Real Real   Real
fabs(.) absolute value Real Real   Real
norm1(.) fabs(real) + fabs(imag) Real      
arg(.) argument Real Real   Real
exp(.) exponential CX CX    
log(.) natural log CX CX   CX
polar_exp(.) exponential Polar Polar    
sqrt(.) square root CX CX   Polar
square(.) square CX Real   Polar
sum_square(.) square of cabs Real Real   Real
sin(.) sin trig. function CX Imag    
cos(.) cos trig. function CX Real    
tan(.) tan trig. function CX Imag    
sinh(.) sinh hyperbolic fn. CX Imag    
cosh(.) cosh hyperbolic fn. CX Real    
tanh(.) tanh hyperbolic fn. CX Imag    

For example:

   CX z = 3.0 + 4.0 * _I_;
   cout << real(z) << ", " << imag(z) << endl;

outputs 3.0, 4.0 .

Only the const versions of real(z) and imag(z) are provided. The comments about the member functions .imag() and .imag_as_imag() also apply here. Note the differences between imag(z), imag_as_imag(z) and Imag(z).

   CX z = 3.0 + 4.0 * _I_;
   Real a = imag(z);            // returns a = 4
   Imag b = imag_as_imag(z);    // returns b = 4i
   Imag c = Imag(z);            // error, can't convert CX to Imag

It is important not to confuse Imag and imag or Real and real.

The functions fabs and cabs are alternative names for the same function.

The function norm1 returns the sum of the absolute values of the real and imaginary parts. This is faster than cabs and may be sufficient for deciding, for example, whether the absolute value of a complex number is less than some bound.

The function polar_exp takes the exp of a complex number and returns the result in polar coordinates.

Conversions

=,  conversions To
From CX Real Imag _I_ Polar
CX Yes       Yes
Real Yes (Yes)     Yes
Imag Yes   Yes   Yes
_I_ Yes   Yes   Yes
Polar Yes       Yes

The only conversions allowed are ones that do not lose information (apart from round-off error). For example, you cannot convert Polar to Imag.

I return void from the = operation so you can't do a = b = c; .

Binary functions

Additive

+, - Second Argument
First Argument CX Real Imag _I_ Polar
CX CX CX CX CX CX
Real CX (Real) Imag Imag CX
Imag CX Imag Imag Imag CX
_I_ CX Imag Imag Imag, Real CX
Polar CX CX CX CX CX

When additions or subtractions involve Polar the Polar number is converted to CX and a CX addition or subtraction performed.

Multiplicative

*, / Second Argument
First Argument CX Real Imag _I_ Polar
CX CX CX CX CX illegal
Real CX (Real) Imag Imag Polar
Imag CX Imag Real Real Polar
_I_ CX Imag Real Real Polar
Polar illegal Polar Polar Polar Polar

Multiplication of a CX variable with a Polar variable will result in a compile error. Either convert the CX variable to Polar, if you want a Polar result or the Polar variable to CX if you want a CX result.

Additive to self

+=, -= Argument
Class CX Real Imag _I_ Polar
CX Yes Yes Yes Yes Yes
Real   (Yes)      
Imag     Yes Yes  
_I_          
Polar Yes Yes Yes Yes Yes

Multiplicative to self

*=, /= Argument
Class CX Real Imag _I_ Polar
CX Yes Yes Yes Yes Yes
Real   (Yes)      
Imag   Yes      
_I_          
Polar Yes Yes Yes Yes Yes

Equality

==, != Second Argument
First Argument CX Real Imag _I_ Polar
CX bool bool bool bool illegal
Real bool (bool) bool false bool
Imag bool bool bool bool bool
_I_ bool false bool true bool
Polar illegal bool bool bool bool

Remember that, because of round-off error, testing equality of floating point variables is not very useful unless the variables involved have values that can be represented exactly as binary numbers or might be equal because one is a copy of the other.

Power

pow Second Argument
First Argument CX Real, int Imag _I_ Polar
CX CX CX CX    
Real CX (Real) CX    
Imag CX CX CX    
_I_          
Polar Polar Polar Polar    

If y is of type int and in the range -12 to 12 then pow(x,y) is calculated by direct multiplication. In all other cases it is calculated with exp(y * log(x)).

Value of pow(x,y) if x is zero:

x y pow(x,y)
0 int and negative throw exception
0 int and non-negative 0
0 not int and non-positive real part throw exception
0 not int and positive real part 0

Make sure you understand what you are doing if you use pow(x,y) with complex y.

Input and output

I have not defined input and output functions.

Global variables, functions

   Real pi, pi_times_2, pi_over_2, pi_over_4

   Real ipow(Real x, int n)
   ImaginaryUnit _I_

The variables pi, pi_times_2, pi_over_2, pi_over_4 have the obvious values.

The global variable _I_ is the representation of i = sqrt(-1).

The function ipow is pow with integer second argument, n. If n is in the range -12 to 12 the value is calculated by multiplication. An version of pow with integer argument is included in the new standard but is not in all compilers.

Mandelbrot set example

The example calculates what is essentially the boundary of the Mandelbrot set using contour integration. Details are given in the program file. The program outputs the coordinates of the points on the boundary. Here is an Excel scatter plot of the points. The program is mostly a demonstration of the CX class but there is a little use of the Imag class and _I_. A copy of the output from the program is in mandel.txt. Your output will probably differ because of the formatting of the output and round-off error.

Mandelbrot set image