Complex numbers (C Plus Plus)

From LiteratePrograms

Jump to: navigation, search
Other implementations: C++ | Java | Ruby | Scala

This article implements a C++ class for representing complex numbers. Although this is a common introductory exercise, in most real programming situations the standard std::complex class should be preferred.

Contents

Data

We choose to use the fixed built-in IEEE floating-point type double to represent the real and imaginary parts of the number. We choose this form instead of another representation such as polar form because it is the only form that makes all four basic arithmetic operations both simple and efficient.

Although we could template the class over the type used for each part, this could make some operations such as division act strangely with certain argument types. The data is made accessible via four accessor methods. We choose "Re" and "Im" after the mathematical operators "Re" and "Im", standing for "real part" and "imaginary part", respectively.

<<complex data members>>=
double real;
double imag;
<<complex declarations>>=
double getRe();
double getIm();
void setRe(double value);
void setIm(double value);
<<complex inline accessors>>=
inline double complex::getRe() { return real; }
inline double complex::getIm() { return imag; }
inline void complex::setRe(double value) { real = value; }
inline void complex::setIm(double value) { imag = value; }

Constructors

We provide one obvious constructor for complex, based on its real and imaginary part. The values are initialized in the member initialization list; this is analogous to writing double real = re; as opposed to double real; real = re;, which is what happens if real is initialized in the constructor body, with { this->real = re; }. Additionally, the constructor is given default arguments of 0.0 for both re and im. This means that if the constructor is called without any arguments, it will produce complex zero, if called with one argument it will produce a conversion from a real number to a complex number, and if given both arguments it produces a complete complex number. The constructor is marked explicit to prevent automatic conversion from a double to a complex. The values are initialized :

<<complex declarations>>=
explicit complex(double = 0.0, double = 0.0);
<<complex inline constructors>>=
explicit inline complex::complex(double re, double im) : real(re), imag(im) { }

Finally, we provide a static method initializing a complex object from the polar form (radius and angle in the plane). It uses this formula to convert to the internal rectangular form:

reiθ = rcosθ + (rsinθ)i
<<complex declarations>>=
static complex fromPolar(double radius, double angle);
<<complex inline constructors>>=
inline static complex complex::fromPolar(double radius, double angle) {
    return complex(radius*cos(angle), radius*sin(angle));
}

We need math.h for sine and cosine on doubles:

<<header files>>=
#include <math.h>

Arithmetic operations

We will implement the four simple arithmetic operations: add, subtract, multiply, and divide. We start with the first three of these, making use of the usual formulas, shown below. C++'s operator overloading makes it relatively simple to make these operations available using the same binary infix syntax used in mathematical notation.

(a + bi) + (c + di) = (a + c) + (b + d)i
(a + bi) − (c + di) = (ac) + (bd)i
<<complex declarations>>=
complex operator+(complex rhs);
complex operator-(complex rhs);
complex operator*(complex rhs);
<<complex inline arithmetic methods>>=
inline complex complex::operator+(complex rhs) {
    return complex(this->real + rhs.real, this->imag + rhs.imag);
}
inline complex complex::operator-(complex rhs) {
    return complex(this->real - rhs.real, this->imag - rhs.imag);
}
inline complex complex::operator*(complex rhs) {
    return complex(this->real*rhs.real - this->imag*rhs.imag,
                   this->real*rhs.imag + this->imag*rhs.real);
}

These operations, especially multiplication, are simpler when one operand is a real number:

<<complex declarations>>=
complex operator+(double rhs);
complex operator-(double rhs);
complex operator*(double rhs);
<<complex inline arithmetic methods>>=
inline complex complex::operator+(double rhs) {
    return complex(this->real + rhs, this->imag);
}
inline complex complex::operator-(double rhs) {
    return complex(this->real - rhs, this->imag);
}
inline complex complex::operator*(double rhs) {
    return complex(this->real * rhs, this->imag * rhs);
}

We also define named methods for the frequently-used operations of taking the complex conjugate and finding the norm (denoted abs(z) or |z|) of a complex number:

<<complex declarations>>=
complex conjugate();
double norm();
<<complex inline basic operations>>=
inline double complex::norm() {
    return this->real*this->real + this->imag*this->imag;
}
inline complex complex::conjugate() {
    return complex(this->real, -this->imag);
}

Division

Division is a bit more complicated. Although there is a general formula, we prefer to simplify things by first defining division by a real number, which, like multiplication by a real number, simply scales each part:

<<complex declarations>>=
complex operator/(double rhs);
<<complex inline arithmetic methods>>=
inline complex complex::operator/(double rhs) {
    return complex(this->real/rhs, this->imag/rhs);
}

Now, we can combine complex multiplication, division by reals, and the complex conjugate and norm operations to perform division of arbitrary complex numbers using this formula:

<<complex declarations>>=
complex operator/(complex rhs);
<<complex inline arithmetic methods>>=
inline complex complex::operator/(complex rhs) {
    return (*this)*rhs.conjugate()/rhs.norm();
}

Note that division by the real or complex number zero is not allowed and will store a floating-point result such as NaN or infinity in the part values, but division by a complex number with only the real or imaginary component zero is okay.

Left-hand side reals

All that remains is the case where doubles are on the left hand side and complex numbers are on the right. However, because the object can only appear on the left-hand side of operator methods that are members of that object, and because we cannot extend built-in types, we must use global (top-level) methods to handle this case. It's a common practice to make such methods friend functions of the class, but this is unneccessary in this case, as they just delegate to previously defined public operations:

<<complex top-level declarations>>=
complex operator+(double lhs, complex rhs);
complex operator-(double lhs, complex rhs);
complex operator*(double lhs, complex rhs);
complex operator/(double lhs, complex rhs);
<<complex inline top-level arithmetic operations>>=
inline complex operator+(double lhs, complex rhs) {
    return rhs + lhs;
}
inline complex operator-(double lhs, complex rhs) {
    return complex(lhs - rhs.getRe(), rhs.getIm());
}
inline complex operator*(double lhs, complex rhs) {
    return rhs * lhs;
}
inline complex operator/(double lhs, complex rhs) {
    return rhs.conjugate()*lhs/rhs.norm();
}

The main simplification possible for division given a real first argument is that we can use the version of multiplication taking a real argument.

Other operations

Complex number computations often make use of certain holomorphic functions that generalize real functions. The simplest is the exponential function, an entire function that can be defined in terms of real-valued functions by:

ea + bi = ea(cosb) + ea(sinb)i

where a,b are real. This can be implemented using some additional functions from math.h:

<<complex top-level declarations>>=
complex exp(complex c);
<<exp function>>=
complex exp(complex c) {
    double e_to_a = exp(c.getRe());
    double b = c.getIm();
    return complex(e_to_a*cos(b), e_to_a*sin(b));
}

Class and files

Here's our complete class and files produced so far:

<<complex.h>>=
#ifndef COMPLEX_H_
#define COMPLEX_H_
header files
class complex {
public:
    complex declarations
private:
    complex data members
};
complex top-level declarations
complex inline accessors
complex inline constructors
complex inline arithmetic methods
complex inline basic operations
complex inline top-level arithmetic operations
#endif // #ifndef COMPLEX_H_
<<complex.cpp>>=
#include "complex.h"
exp function
Download code
Views