Complex numbers (C Plus Plus)
- Other implementations: C++ | Java
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 |
[edit] 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; }
[edit] Constructors
We provide one obvious constructor for complex
, based on its real and imaginary part, using this explicitly for clarity:
<<complex declarations>>= complex(double re, double im); <<complex inline constructors>>= inline complex::complex(double re, double im) { this->real = re; this->imag = im; }
We also provide a default constructor yielding complex zero, a conversion constructor from a single real number (this is marked explicit to avoid confusing implicit coercions from double
to complex
):
<<complex declarations>>= complex(); explicit complex(double re); <<complex inline constructors>>= inline complex::complex() { this->real = 0.0; this->imag = 0.0; } inline complex::complex(double re) { this->real = re; this->imag = 0.0; }
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:
- re^{iθ} = rcosθ + (rsinθ)i
<<complex declarations>>= static complex fromPolar(double radius, double angle); <<complex inline constructors>>= inline 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>
[edit] 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 + b) + (c + d)i
- (a + bi) − (c + di) = (a − b) + (c − d)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); }
[edit] 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.
[edit] 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.
[edit] 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:
- e^{a + bi} = e^{a}(cosb) + e^{a}(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)); }
[edit] 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 |