Clement's web pages

A complex number class in C++

Une classe pour représenter les nombres complexes en C++.

Yet another complex number class in C++

Here is an atempt to provide a simple and correct complex class in C++. It's a bit more complete than the standart std::complex<double> class on the operator side, but not for the provided functions.

It provides operators +, -, /, *, = (and their compounds) ++, --, !=, <, >, <= and >= so that you can write things like

Complex a = -5 * J + 1.34;

It also provides I/O operators >> and <<, with several syntaxes:
-7.2+5.4i
.5 - 1e2J (= 0.5-100i)
1 + -1 i (= 1-1i) 

Compile with gcc4, gcc4-apple and MSVS 2005. 

Content of complex.h file:

#ifndef CPX_COMPLEX_H
#define CPX_COMPLEX_H 1

#include <iostream>
#include <iomanip>
#include <cmath>
#include <limits>

namespace cpx {

// Complex number class
class Complex
{
  public:
  // Constructors
    Complex();
    Complex( double );
    Complex( double, double );
    Complex( const Complex& );

  // Destructor
    ~Complex();

  // Selectors
    double real() const;
    double imag() const;

  // Modifiers
    void set( double, double );

  // Operator overloads
    // assignment & arithmetic-assignment operators
    Complex& Complex::operator=( const Complex& );
    Complex& Complex::operator=( const double& );
    Complex& Complex::operator+=( const Complex& );
    Complex& Complex::operator+=( const double& );
    Complex& Complex::operator-=( const Complex& );
    Complex& Complex::operator-=( const double& );
    Complex& Complex::operator*=( const Complex& );
    Complex& Complex::operator*=( const double& );
    Complex& Complex::operator/=( const Complex& );
    Complex& Complex::operator/=( const double& );

    // increment & decrement operators (adds/remove 1 to/from the real part)
    Complex& operator++();    // prefix ++ returns a reference
    Complex operator++( int );  // postfix ++ returns a value
    Complex& operator--();
    Complex operator--( int );
   
    // stream operators are our friends
    friend std::ostream& operator <<(std::ostream &, const Complex &);
    friend std::istream& operator >>(std::istream &, Complex &);

  private:

  // Member variables
    double re;  // real part
    double im;  // imaginary part
};

// Nonmember operators (to allow implicit conversion of the left operand)
// Note: doubles are considered as complex with no imaginary part

Complex operator+( const Complex&, const Complex& );
Complex operator+( const Complex&, const double& );
Complex operator+( const double&, const Complex& );
Complex operator+( const Complex& ); // unary + operator
   
Complex operator-( const Complex&, const Complex& );
Complex operator-( const Complex&, const double& );
Complex operator-( const double&, const Complex& );
Complex operator-( const Complex& ); // unary - operator
   
Complex operator*( const Complex&, const Complex& );
Complex operator*( const Complex&, const double& );
Complex operator*( const double&, const Complex& );

//Division operator returns + or - infinity if the right operand is zero
Complex operator/( const Complex&, const Complex& );
Complex operator/( const Complex&, const double& );
Complex operator/( const double&, const Complex& );
   
bool operator==( const Complex&, const Complex& );
bool operator==( const Complex&, const double& );
bool operator==( const double&, const Complex& );

bool operator!=( const Complex&, const Complex& );
bool operator!=( const Complex&, const double& );
bool operator!=( const double&, const Complex& );

// Comparison between complex are actually comparison of their module (inline function abs beloz)
bool operator<( const Complex&, const Complex& );
bool operator>( const Complex&, const Complex& );
bool operator<=( const Complex&, const Complex& );
bool operator>=( const Complex&, const Complex& );

// Other non-mermber function

// real(c) is more natural than c.real() for some people, so we provide both
inline double real( const Complex& c ) {
  return c.real();
}

inline double imag( const Complex& c ) {
  return c.imag();
}

// Imaginary units defined in complex.cpp

extern const Complex I;
extern const Complex& J; // J is an alias for I

} // end namespace cpx

namespace std {
  // overload of the std::abs function for complex
  inline double abs( const cpx::Complex& c ) {
    return sqrt( c.real() * c.real() + c.imag() * c.imag() );
  }
}

#endif

Content of the complex.cpp file :

#include "complex.h"

using namespace std;

namespace {
  // Used to compare 2 values of type double
  // Two double are considered as "equals" if their difference is significantly
  // smaller than themselves.
  // Derived from FAQ C++ Lite, http://www.parashift.com/c++-faq-lite/ section 29.17
  // see "The Art of Computer Programming", by Donald E. Knuth, section 4.2.2 pages 217-218
  inline bool is_equal( const double& x, const double& y )
  {
    static const double epsilon = numeric_limits<double>::epsilon();
    const double diff = abs( x - y );
    return ( diff <= epsilon * abs(x) || diff <= epsilon * abs(y) );
    // we have to take <= and not < in case difference x and y are both 0
  }

  const double infinity = numeric_limits<double>::infinity();
}

namespace cpx {

Complex::Complex()
{
}
Complex::Complex( double re )
{
  this->re = re;
  this->im = 0;
}
Complex::Complex( double re, double im )
{
  this->re = re;
  this->im = im;
}

Complex::~Complex()
{
}

Complex::Complex( const Complex& c )
{
  re = c.re;
  im = c.im;
}

double Complex::real() const
{
  return re;
}

double Complex::imag() const
{
  return im;
}

void Complex::set( double real, double imaginary )
{
  re = real;
  im = imaginary;
}

Complex& Complex::operator=( const Complex& c )
{
  if (this == &c) return *this;
  re = c.re;
  im = c.im;
  return (*this);
}
Complex& Complex::operator=( const double& d )
{
  re = d;
  im = 0;
  return (*this);
}

Complex& Complex::operator+=( const Complex& c )
{
  re += c.re;
  im += c.im;
  return (*this);
}
Complex& Complex::operator+=( const double& d )
{
  re += d;
  return (*this);
}

Complex& Complex::operator-=( const Complex& c )
{
  re -= c.re;
  im -= c.im;
  return (*this);
}
Complex& Complex::operator-=( const double& d )
{
  re -= d;
  return (*this);
}

Complex& Complex::operator*=( const Complex& c )
{
  double re2 = re;      // backup re, as we're going to modify it
  double cre2 = re;     // backup c.re too, in case c *= c;
  re = re  * c.re - im * c.im;
  im = re2 * c.im + im * cre2;
  return (*this);
}
Complex& Complex::operator*=( const double& d )
{
  re *= d; // same as above with c.im = 0 and c.re = d
  im *= d; // logically and happily simplified
  return (*this);
}

Complex& Complex::operator/=( const Complex& c )
{
  if (this == &c) {     // c /= c;
    re = 1;
    im = 0;
  }
  else if (c == 0) {
    re = (re < 0) ? -infinity : infinity;
    im = (im < 0) ? -infinity : infinity;
  }
  else {
    double re2 = re;    // backup re, as we're going to modify it
                        // no need to backup c.re here
    re = ( re * c.re + im  * c.im ) / ( c.re * c.re + c.im * c.im );
    im = ( im * c.re - re2 * c.im ) / ( c.re * c.re + c.im * c.im );
  }
  return (*this);
}
Complex& Complex::operator/=( const double& d )
{
  if( is_equal(d, 0) ) {
    re = (re < 0) ? -infinity : infinity;
    im = (im < 0) ? -infinity : infinity;
  }
  re /= d;
  im /= d;
  return (*this);
}

Complex& Complex::operator++()
{
  ++re;
  return (*this);
}
Complex Complex::operator++( int )
{
  Complex tmp (*this);
  ++( *this );
  return tmp;
}

Complex& Complex::operator--() {
  --re;
  return (*this);
}
Complex Complex::operator--( int )
{
  Complex tmp (*this);
  --( *this );
  return tmp;
}

// Prints out a complex with the form a+bi or a-bi or 0
ostream& operator<<( ostream& s, const Complex& c )
{
  s << c.re << setiosflags( ios::showpos );
  if( c.im != 0 ) {
    s << c.im << "i";
  }
  s << resetiosflags ( ios::showpos );
 
  return s;
}

// Reads a complex number c into the input stream s.
// c has to be the form: number + ('+' or '-') + number + ('i' or 'j' or 'I' or 'J')
// spaces can be used between the elements, but not inside an element ("- 7+2i" is incorrect)
// ex: valid: 1.2+5i ; 1e2-.5J (= 100-0.5i) ; -4 + -8 i (= -4-8i)
//  invalid : - 7+5J (space inside the first number) ; 7i-4 (order)
// If bad input is encountered, s.setstate(ios::failbit) is called.
istream& operator>>( istream& s, Complex& c )
{
  char op, ij;
  if( !( (s >> c.re >> op >> c.im >> ij)
    && (op == '+' || op == '-')
    && (ij == 'i' || ij == 'I' || ij == 'j' || ij == 'J') ) )
  {
    s.setstate( ios::failbit );
  }
  else if( op == '-' && c.im != 0 ) {
    c.im *= -1;
  }

  return s;
}

Complex operator+( const Complex& lhs, const Complex& rhs ) {
  return Complex( real(lhs) + real(rhs) , imag(lhs) + imag(rhs) );
}
Complex operator+( const Complex& lhs, const double& rhs ) {
  return Complex( real(lhs) + rhs , imag(lhs) );
}
Complex operator+( const double& lhs, const Complex& rhs ) {
  return Complex( lhs + real(rhs) , imag(rhs) );
}
Complex operator+( const Complex& lhs ) {
  return Complex( lhs );
}

Complex operator-( const Complex& lhs, const Complex& rhs ) {
  return Complex( real(lhs) - real(rhs) , imag(lhs) - imag(rhs) );
}
Complex operator-( const Complex& lhs, const double& rhs ) {
  return Complex( real(lhs) - rhs , imag(lhs) );
}
Complex operator-( const double& lhs, const Complex& rhs ) {
  return Complex( lhs - real(rhs) , - imag(rhs) );
}
Complex operator-( const Complex& c ) {
  return Complex( - real(c) , - imag(c) );
}

Complex operator*( const Complex& lhs, const Complex& rhs ) {
  return Complex(
    real(lhs) * real(rhs) - imag(lhs) * imag(rhs),
    real(lhs) * imag(rhs) + imag(lhs) * real(rhs)
  );
}
Complex operator*( const Complex& lhs, const double& rhs ) {
  return Complex( real(lhs) * rhs, imag(lhs) * rhs );
}
Complex operator*( const double& lhs, const Complex& rhs ) {
  return Complex(  lhs * real(rhs), lhs * imag(rhs) );
}

// Division operator returns + or - infinity if the right operand is zero
Complex operator/( const Complex& lhs, const Complex& rhs ) {
  if( rhs == 0 )
    return Complex(
      ( real(lhs) < 0 ) ? -infinity : infinity,
      ( imag(lhs) < 0 ) ? -infinity : infinity
    );
  else return Complex(
    ( real(lhs) * real(rhs) + imag(lhs) * imag(rhs) ) / ( real(rhs) * real(rhs) + imag(rhs) * imag(rhs) ),
    ( imag(lhs) * real(rhs) - real(lhs) * imag(rhs) ) / ( real(rhs) * real(rhs) + imag(rhs) * imag(rhs) )
  );
}
Complex operator/( const Complex& lhs, const double& rhs ) {
  if( is_equal(rhs, 0) )
    return Complex(
      ( lhs < 0 ) ? -infinity : infinity,
      infinity
    );
  else return Complex(
    ( real(lhs) * rhs ) / ( rhs * rhs ),
    ( imag(lhs) * rhs ) / ( rhs * rhs )
  );
}
Complex operator/( const double& lhs, const Complex& rhs ) {
  if( rhs == 0 )
    return Complex(
      ( real(lhs) < 0 ) ? -infinity : infinity,
      ( imag(lhs) < 0 ) ? -infinity : infinity
    );
  else return Complex(
    (   lhs * real(rhs) ) / ( real(rhs) * real(rhs) + imag(rhs) * imag(rhs) ),
    ( - lhs * imag(rhs) ) / ( real(rhs) * real(rhs) + imag(rhs) * imag(rhs) )
  );
}

bool operator==( const Complex& lhs, const Complex& rhs ) {
  return ( is_equal( real(lhs), real(rhs) ) && is_equal( imag(lhs), imag(rhs) ) );
}
bool operator==( const Complex& lhs, const double& rhs ) {
  return ( is_equal( real(lhs), rhs ) && is_equal( imag(lhs), 0 ) );
}
bool operator==( const double& lhs, const Complex& rhs ) {
  return ( is_equal( lhs, real(rhs) ) && is_equal( 0, imag(rhs) ) );
}

bool operator!=( const Complex& lhs, const Complex& rhs ) {
  return !( lhs == rhs );
}
bool operator!=( const Complex& lhs, const double& rhs ) {
  return !( lhs == rhs );
}
bool operator!=( const double& lhs, const Complex& rhs ) {
  return !( lhs == rhs );
}

bool operator<( const Complex& lhs, const Complex& rhs ) {
  return ( abs(lhs) < abs(rhs) );
}
bool operator>( const Complex& lhs, const Complex& rhs ) {
  return ( abs(lhs) > abs(rhs) );
}
bool operator<=( const Complex& lhs, const Complex& rhs ) {
  return ( lhs < rhs || lhs == rhs );
}
bool operator>=( const Complex& lhs, const Complex& rhs ) {
  return ( lhs > rhs || lhs == rhs );
}

// Imaginary units declared in complex.h
const Complex I(0, 1);
const Complex& J = I; // J is an alias for I

} // end namespace cpx

If you have any question or comment regarding this code, feel free to email me.