Complex Numbers

Math makes folks uncomfortable! Well, trust me, you will wish you had paid more attention in your math classes the more you stay with Computer Science.

Let’s explore Complex Numbers and see how to set them up in C++. In doing this we will see how to extend your classes so you can use the normal mathematical operators to combine them (assuming that makes sense!).

Square Root of -1

You were probably told you cannot take the square root of a negative number. You can, it just produces something called an imaginary number:

i = \sqrt{-1}

A complex number is made up of a “real” part, and an “imaginary” part:

3 + 4i

We use the “i” to indicate the “imaginary” part. The first part is the “real” part.

Addition

We can add two complex numbers by combining the real and imaginary parts:

(a + bi) + (c + di) = (a + b ) + (b + d)i

Subtraction

Subtraction works the same way:

(a + bi) - (c + di) = (a - c) + (b - d)i

The usual rules apply if you get a negative number for the imaginary part!

Multiplication

Multiplication is a bit tougher, because we have to consider two cases:

(a + bi)(c + di) = (ac +adi) + (bci - bd)

Remember that ii = -1

This simplifies to :

(a + bi)(c + di) = (ac - bd) + (ad + bc)i

There is another form of multiplication to consider. When we multiply a complex number by a scalar (constant) we get this:

A(a + bi) = Aa + Abi

Division

This one gets messy!

First, let’s deal with “i” in the denominator:

\frac{a}{bi} = \frac{ai}{bii} = \frac{ai}{-b} = -\frac{ai}{b}

With that out of the way, let’s try to figure out division of complex numbers:

\frac{a + bi}{c + di}

What are we going to do with this?

Complex Conjugates

Before we get to that, let’s look at something called a “complex conjugate” of a complex number.

The complex conjugate of a complex number is identical to that number with the sign on the imaginary part reversed. Showing the complex conjugate in math books is often done by placing a bar over the number:

\overline{a + bi} = a - bi

What makes this useful is this:

(a + bi)\overline{(a + bi)} = (a + bi)(a - bi) = aa - abi + abi - bbii = a^2 + b^2

Well, if we multiply the top and bottom by the “complex conjugate” of the denominator, we get something interesting:

\frac{a + bi}{c + di} = \frac{(a + bi)(c - di)}{(c + di)(c - di)}
= \frac{(ac - adi + bci -bdi^2)}{(c^2 + d^2)}
= \frac{ (ac + bd) + (bc - ad)i }{(c^2 + d^2)}

Finally, dividing a complex number by a constant is simple:

\frac{a + bi}{B} = \frac{a}{B} + \frac{b}{B}i

Now we have enough math to develop a complex number class.

C++ Complex Class

Obviously, our complex number class needs two attributes, and several methods. We will start off with the basic constructors:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// Complex.h - complex number class
#include <iostream>
using namespace std;

class Complex {
    private:
        double r;       // real part
        double i;       // imaginary part

    public:
        Complex();      // default constructor
        Complex( double r, double i );  // normal constructor
        Complex( double r );            // alternate constructor
        
        Complex operator * ( const Complex & rhs );
        friend Complex operator + ( double lhs, const Complex & rhs );

Displaying Complex Numbers

In order to make displaying complex numbers simple, we need to build a routine that lets us use the normal C++ output operator: <<. This operator is actually used by output “stream” objects (remember that iostream you include in your code?).

Friend Functions

To create a function that will let us display complex numbers we need to use a special kind of function called a friend. Friend functions are declared inside a class, but they are “global”, meaning they can be called anywhere. In the case of operators, C++ looks at the two objects on either side of the operator and looks for a method with operands that match the types it finds.

Friend methods have full access to all of the private data in the class.

Here is the prototype for the friend function we need. This method needs to be a public method in our Complex class:

friend ostream & operator << ( ostream & lhs, const Complex & rhs );

The pattern of this method is something we will see again. The name of the method is those funny “<<” characters. The “operator” in front of that tells the compiler what you are doing. We send two parameters to this method, The lhs is the object in the left side of the operator. In our case, that will be the “stream” we want to send our object to. The rhs parameter needs to be marked as const, which tells the compiler not to mess with it. That object will be made available inside the method, where we can inspect it, and figure out what to do to display that object.

Basically this operator looks like this in action:

cout << complex_number

or

lhs << rhs;

In this example, the operator function is being called between an ostream object and a Complex object. These two objects are passed as parameters to our friend function. In this example, the lhs is an ostream object (cout), and the rhs object is our complex number.

Here is the implementation of our class so far:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <sstream>
using namespace std;
#include "Complex.h"

// constructors -------------------------------------------
Complex::Complex() {
    r = 0;
    i = 0;
}

Complex::Complex( double r, double i ) {
    this->r = r;
    this->i = i;
}

Complex::Complex( double r ) {
    this->r = r;
    i = 0;
}

// accessors ----------------------------------------------

string Complex::toString( void ) {
    ostringstream  tmp;
    tmp << r <<  "+" << i << "i";
    return temp;
}

Complex Complex::operator / ( const Complex & rhs ) {
    Complex temp;

Completing this class for the missing methods should be fairly simple. Here is my test code:

#include <iostream>
#include "Complex.h"
using namespace std;

int main( int argc, char * argv[] ) {
    Complex a;
    Complex b(2.0);
    Complex c(3,5);

    cout << a << ' ' << b << ' ' << b + c << ' ' << c << endl;
    cout << a + 1.5  << ' ' << 2.5 + b << ' ' << b - c << ' ' << c << endl;
}

You should test your code using example problems you can find on the Internet!