Department of Engineering

IT Services

C++ complex matters arising

The Good News

C++'s complex class greatly simplifies the use of complex numbers and is well integrated into other parts of the standard library. Once complex numbers are created they can be used in most situations much as simple integers can. The type of the real and imaginary parts is under user control and makes use of template notation. Here's a little example

#include <iostream>
#include <complex>
using namespace std;
int main() {
 complex<double> c1(0.0,1.0), c2; // complex numbers with double components
 c2=pow(c1,2.0);
 cout << "The square of " << c1 << " is " << c2 << endl; 
}

Run this and you'll see that i squared just about equals -1. So far so good. Let's try to assign values to these complex numbers. How about

c2=2;

No problem - printing c2 out gives (2,0) which is fair enough.

The Bad News

Using <complex> isn't quite as simple as it first looks, and even the 3rd edition of Deitel & Deitel's book doesn't mention it. There are some traps for the unwary that aren't all specific to complex numbers but users are likely to meet them first in this context.

Assignment

Suppose you try to set a complex number using

c2=(3,7);

This is legal, but printing c2 out gives (7,0). Surprized? Were you expecting (3,7)? In fact, c2=(3,7) is equivalent to c2=3,7. C++'s "," operator suppresses the value of the first operand in favour of the second, so c2=3,7 is equivalent to c2=7. However, you can input a complex number as "(3,7)" when reading from a file - for example with the following code

...
 complex<double> c1; // complex numbers with double components
 cout << "Type in a complex number: ";
 cin >> c1;
 cout << "You typed " << c1 << endl; 
...

Mixing types

There's unexpected behaviour (with my compiler anyway) when mixing types. The following compiles

#include <complex>
using namespace std;

int main()
{
  complex<int> c_int(2,3);
  c_int= c_int*2;
}

but this doesn't

#include <complex>
using namespace std;

int main()
{
  complex<double> c_double(2,3);
  c_double= c_double*2;
}

though this does

#include <complex>
using namespace std;

int main()
{
  complex<double> c_double(2,3);
  complex<int> c_int(2,3);
  c_double*=2;
  c_double=c_int;
}

Also there are differences between what's allowed by constructors and assignments as illustrated by this code (from Stroustrup's errata to his 6th edition).

#include <complex>
using namespace std;

void f(complex< float> cf, complex< double> cd, 
       complex< long double> cld, complex< int> ci){
  complex<double> c1 = cf;  // fine
  complex<double> c2 = cd;  // fine
  complex<double> c3 = cld; // error: possible truncation
  complex<double> c4(cld);  // ok: explicit conversion
  complex<double> c5 = ci;  // error: no conversion

  c1 = cld; // ok, but beware: possible truncation
  c1 = cf;  // ok
  c1 = ci;  // ok
}

int main()
{
  complex<int>         c_int;
  complex<float>       c_float;
  complex<double>      c_double;
  complex<long double> c_longdouble;
  f(c_float, c_double, c_longdouble, c_int );
}

According to Matt Austern "The C++ Standard, section 26.2, paragraph 2, says 'The effect of instantiating the template complex for any type other than float, double, or long double is unspecified.'" which explains some of the above problems. See his The Standard Librarian: Complex Numbers for details.

>>

If you want to create a list of integers, you just do

  list<int> L;

but if you try to create a list of complex numbers with

  list<complex<int>> L;

the compiler will moan. The problem is that it treats >> as the bit-shift operator - you need to add a space between the > characters.