Mathematics
f(z)    Complex Numbers and Functions     Tech Note
Complex Arithmetic

This page gives an overview of complex arithmetic, including addition, subtraction, multiplication, and division. In addition, several other concepts are explained including complex conjugate, equality testing, and negation.

Given

Example

Complex arithmetic:  CAdd, CSub, CMult, CDiv
 
Let  u =   1.000000000 +  1.000000000i  =   1.414213562 * CIS(  0.785398163)
     v =   1.732050808 -  1.000000000i  =   2.000000000 * CIS( -0.523598776)

CAdd:  Complex addition

u + v = (a + ib) + (c + id) = (a + c) + i(b + d)

CAdd function

// z = a + b
FUNCTION CAdd  (CONST a,b:  TComplex):  TComplex;
  VAR
    aTemp:  TComplex;
    bTemp:  TComplex;
BEGIN
  // Can't add values if in cfPolar form.
  // Convert to cfRectangular if necessary.
  aTemp := CConvert(a, cfRectangular);
  bTemp := CConvert(b, cfRectangular);
  RESULT.form := cfRectangular;
  RESULT.x := aTemp.x + bTemp.x;   // real part
  RESULT.y := aTemp.y + bTemp.y    // imaginary part
END {CAdd};

Example

u + v =   2.732050808 +  0.000000000i  =   2.732050808 * CIS(  0.000000000)

 

CSub:  Complex subtraction

u - v = (a + ib) - (c + id) = (a - c) + i(b - d)

CSub function

// z = a - b
FUNCTION CSub  (CONST a,b:  TComplex):  TComplex;
  VAR
    aTemp:  TComplex;
    bTemp:  TComplex;
BEGIN
  aTemp := CConvert (a,cfRectangular);
  bTemp := CConvert (b,cfRectangular);
  RESULT.form := cfRectangular;
  RESULT.x := aTemp.x - bTemp.x;   // real part
  RESULT.y := aTemp.y - bTemp.y;   // imaginary part
END {CSub};

Example

u - v =  -0.732050808 +  2.000000000i  =   2.129764866 * CIS(  1.921675738)

 

CMult:  Complex multiplication

c × u = c(a + ib) = ac + i(bc)

u × v = (a + ib) (c + id) = (ac – bd) + i(ad + bc)


-p < q1 + q2 £ p

CMult function

// z = a * b
FUNCTION CMult(CONST a,b:  TComplex):  TComplex;
  VAR
    bTemp:  TComplex;
BEGIN
  // arbitrarily convert one to type of other
  // (no conversion if both are the same)
  bTemp := CConvert(b, a.form);
 
  RESULT.form := a.form;
  CASE a.form OF
    cfPolar:
      BEGIN
        RESULT.r := a.r * bTemp.r;
        RESULT.theta := FixAngle(a.theta + bTemp.theta)
      END;
 
    cfRectangular:
      BEGIN
        RESULT.x := a.x*bTemp.x - a.y*bTemp.y;
        RESULT.y := a.x*bTemp.y + a.y*bTemp.x
      END
  END
END {CMult};

Example

u * v =   2.732050808 +  0.732050808i  =   2.828427125 * CIS(  0.261799388)

The formulas for complex multiplication and division are somewhat simpler in exponential or polar form than the rectangular form.

When angles are added (or subtracted), such as in the multiplication (or division) of complex polar numbers, they should be remapped to the interval (-p .. p] when necessary.  The FixAngle function does this.

FixAngle function

// -PI < theta <= PI
FUNCTION FixAngle (CONST theta:  TReal):  TReal;
BEGIN
  RESULT := theta;
 
  WHILE RESULT > PI DO
  BEGIN
    RESULT := RESULT - TwoPI;
  END;
 
  WHILE RESULT <= -PI DO
  BEGIN
    RESULT := RESULT + TwoPI;
  END
 
END {FixAngle};

Results from FixAngle function

FixAngle -- keep angle in interval (-PI..PI]
 
  Radians     FixAngle
-----------  ----------
-9.424778   -3.141593
-7.853982   -1.570796
-6.283185    0.000000
-4.712389    1.570796
-3.141593   -3.141593
-1.570796   -1.570796
 0.000000    0.000000
 1.570796    1.570796
 3.141593    3.141593
 4.712389   -1.570796
 6.283185    0.000000
 7.853982    1.570796
 9.424778    3.141593
10.995574   -1.570796
12.566371    0.000000
14.137167    1.570796
15.707963    3.141593
17.278760   -1.570796
18.849556    0.000000

 

CDiv:  Complex division


where


-p < q1 ­– q2 £ p

CDiv function

// z = a / b
FUNCTION CDiv  (CONST a,b:  TComplex):  TComplex;
  VAR
    bTemp:  TComplex;
    SumSquares:  TReal;
BEGIN
  // arbitrarily convert one to type of other
  // (no conversion if both are the same)
  bTemp := CConvert(b, a.form);
 
  RESULT.form := a.form;
  CASE a.form OF
    cfPolar:
      BEGIN
        TRY
          RESULT.r := a.r / bTemp.r;
          RESULT.theta := FixAngle(a.theta - bTemp.theta)
        EXCEPT
          ON EZeroDivide DO
             RAISE EComplexZeroDivide.Create('Polar r/0');
          ON EInvalidOp DO
             RAISE EComplexInvalidOp.Create('Polar 0/0');
        END
      END;
 
    cfRectangular:
      BEGIN
        TRY
          SumSquares := SQR(bTemp.x) + SQR(bTemp.y);
          RESULT.x := (a.x*bTemp.x + a.y*bTemp.y) / SumSquares;
          RESULT.y := (a.y*bTemp.x - a.x*bTemp.y) / SumSquares
        EXCEPT
          ON  EZeroDivide DO
              RAISE EComplexZeroDivide.Create('Rectangular x/0');
          ON  EInvalidOp DO
              RAISE EComplexInvalidOp.Create('Rectangular 0/0')
        END
      END
  END
END {CDiv};

Example

u / v =   0.183012702 +  0.683012702i  =   0.707106781 * CIS(  1.308996939)

The code for the above examples is shown next:

Code for complex arithmetic examples

PROCEDURE TFormComplexMath.ComplexArithmetic1;
  VAR
    u:  TComplex;
    v:  TComplex;
    z:  TComplex;
 
begin
  Memo.Lines.Add('Complex arithmetic:  CAdd, CSub, CMult, CDiv');
  Memo.Lines.Add('');
 
  u := CSet(SQRT(2), PI/4, cfPolar);
  Memo.Lines.Add('Let  u = ' + CToRectStr(u,13,9) + '  = ' +
                               CToPolarStr(u,13,9));
  v := CSet(SQRT(3), -1);
  Memo.Lines.Add('     v = ' + CToRectStr(v,13,9) + '  = ' +
                               CToPolarStr(v,13,9));
  Memo.Lines.Add('');
 
  z := CAdd(u, v);
  Memo.Lines.Add(' u + v = ' + CToRectStr(z,13,9) + '  = ' +
                               CToPolarStr(z,13,9));
 
  z := CSub(u, v);
  Memo.Lines.Add(' u - v = ' + CToRectStr(z,13,9) + '  = ' +
                               CToPolarStr(z,13,9));
 
  z := CMult(u, v);
  Memo.Lines.Add(' u * v = ' + CToRectStr(z,13,9) + '  = ' +
                               CToPolarStr(z,13,9));
 
  z := CDiv(u, v);
  Memo.Lines.Add(' u / v = ' + CToRectStr(z,13,9) + '  = ' +
                               CToPolarStr(z,13,9));
 
  Memo.Lines.Add('');
 
 . . .
END {Arithmetic1};

 

CConjugate:   Complex conjugate

The above expression for division using rectangular coordinates suggests introducing the concept of a complex conjugate.  The complex conjugate of v = c + id  is given the symbol  (or v* ) , where = c – id.   

Interestingly, the conjugate of a complex number in polar form can also be formed by only negating the angular coordinate.  That is, if z = (r, q), then z* = (r, -q).  This result can be derived form the expression

and the identities:


CConjugate function

// z = a*
FUNCTION CConjugate (CONST a:  TComplex):  TComplex;
BEGIN
  RESULT.form := a.form;
  CASE a.form OF
    cfPolar:
      BEGIN
        RESULT.r := a.r;
        RESULT.theta := FixAngle(-a.theta)
      END;
 
    cfRectangular:
      BEGIN
        RESULT.x := a.x;
        RESULT.y := -a.y
      END
  END
END {CConjugate};

CConjugate function results

Complex conjugate:  z*
 
                          z*
     z                rectangular
------------  ----------------------------
 0.0 +  0.0i   0.000000000 +  0.000000000i
 0.5 +  0.5i   0.500000000 -  0.500000000i
-0.5 +  0.5i  -0.500000000 -  0.500000000i
-0.5 -  0.5i  -0.500000000 +  0.500000000i
 0.5 -  0.5i   0.500000000 +  0.500000000i
 1.0 +  0.0i   1.000000000 +  0.000000000i
 1.0 +  1.0i   1.000000000 -  1.000000000i
 0.0 +  1.0i   0.000000000 -  1.000000000i
-1.0 +  1.0i  -1.000000000 -  1.000000000i
-1.0 +  0.0i  -1.000000000 +  0.000000000i
-1.0 -  1.0i  -1.000000000 +  1.000000000i
 0.0 -  1.0i   0.000000000 +  1.000000000i
 1.0 -  1.0i   1.000000000 +  1.000000000i
 5.0 +  0.0i   5.000000000 +  0.000000000i
 5.0 +  3.0i   5.000000000 -  3.000000000i
 0.0 +  3.0i   0.000000000 -  3.000000000i
-5.0 +  3.0i  -5.000000000 -  3.000000000i
-5.0 +  0.0i  -5.000000000 +  0.000000000i
-5.0 -  3.0i  -5.000000000 +  3.000000000i
 0.0 -  3.0i   0.000000000 +  3.000000000i
-5.0 -  3.0i  -5.000000000 +  3.000000000i

The product of a complex value and its conjugate is always a real number, e.g.,

 

This value is the square of the distance from the origin to v.  The square root of this value is known as the “norm,” magnitude, modulus, or absolute value of a complex value.  That is,

This means the complex quotient above with CDiv could be written as

This form is not intended to be “simpler” but rather help explain the concepts of complex conjugate and magnitude of a complex value.

 

Complex equality testing:  CIsSame, CIsZero, CDeFuzz

u = v, or

a + ib = c + id 

if and only if a = c, and b = d

Other comparisons, e.g., less than (<) or greater than (>), are not defined for complex values.  However, the absolute value, |z| = | x + iy |, which is also often written as abs(z), allows a way to compare the magnitude of complex values.

While the comparison of  0 + i0 and 2 +i2 is undefined, the magnitude of the values can be compared:

| 0 + i0 |  <  | 2 + i2 |.

The absolute value of a complex number equals the absolute value of its conjugate.  That is,

Before explaining the functions used to test for complex equality, CIsSame, CDeFuzz, and CIsZero, the concept of “fuzz” on floating-point values must be introduced. 

Floating-point values in computers only approximate the concept of a “real” number from mathematics since floating-point values only have limited precision.  Often after computations, such as the loss of precision in the subtraction of like values, values that should be zero are not. 

The APL language introduced the concept of  “fuzz”.  A value smaller than the fuzz value was changed to zero.  This especially helped the display after calculations when some extremely small values should be interpreted as zero.  But the exact fuzz value, is application specific and no one value is appropriate for all applications.  The SetFuzz procedure in the ComplexMathLibrary unit can be used to establish a new value for “fuzz”.  The default value is fuzz := 1 x 10-12.

A DeFuzz routine “fixes” small values to be zero instead of a small fuzzy value.  A similar routine works with complex numbers and is useful in the comparison of whether two complex numbers are equal.

DeFuzz function

FUNCTION  DeFuzz (CONST x:  TReal):  TReal;
BEGIN
  IF   ABS(x) < fuzz
  THEN Defuzz := 0
  ELSE Defuzz := x
END {Defuzz};

CDefuzz:  complex DeFuzz function

FUNCTION CDeFuzz (CONST z:  TComplex):  TComplex;
BEGIN
  CASE z.form OF
    cfRectangular:
      BEGIN
        RESULT.form := cfRectangular;
        RESULT.x := DeFuzz(z.x);
        RESULT.y := DeFuzz(z.y);
      END;
 
    cfPolar:
      BEGIN
        RESULT.form := cfPolar;
        RESULT.r := DeFuzz(z.r);
        IF   RESULT.r = 0.0
        THEN RESULT.theta := 0.0
        // canonical form when radius is zero
        ELSE RESULT.theta := DeFuzz(z.theta)
      END;
  END
END {CDeFuzz};

With a default fuzz := 1 x 10-12, the value z below should be equivalent to a complex 0.

Sample code using CDefuzz

z := CSet(-3.21E-14, 7.65E-14);
Memo.Lines.Add('  Before:  ' + Format('%18.15f + %18.15fi',
                                      [z.x, z.y]) );
IF   NOT CIsZero(z)
THEN Memo.Lines.Add('    not zero');
 
z := CDeFuzz(z);
Memo.Lines.Add('   After:  ' + Format('%18.15f + %18.15fi',
                                      [z.x, z.y]) );
IF   CIsZero(z)
THEN Memo.Lines.Add('  IS zero');

The code above gives the following results:

Example output

Miscellaneous:  Floating-point "fuzz"
 
  Before:  -0.000000000000032 +  0.000000000000077i
   After:   0.000000000000000 +  0.000000000000000i
  IS zero

This suggests an easy way to compare two complex values, z1 and z2, for equality is to compute the difference in the values, z1 – z2, and then “DeFuzz” this difference.  This approach is used in both the CIsZero and CIsSame routines:

CIsZero and CIsSame routines

FUNCTION CIsSame(CONST z1,z2:  TComplex):  BOOLEAN;
  VAR
    u1,u2:  TComplex;
BEGIN
  u1 := CConvert(z1, cfRectangular);
  u2 := CConvert(z2, cfRectangular);
 
  RESULT := (CAbs(CDeFuzz(CSub(u1,u2)) ) = 0.0)
END {CIsSame};
 
 
FUNCTION CIsZero(CONST z:  TComplex):  BOOLEAN;
BEGIN
  RESULT := (CAbs(CDeFuzz(z)) = 0.0)
END {CIsZero};

 

CNeg:  Negation

Negation is perhaps the simplest operation that can be performed in either rectangular or polar form.  To negate the rectangular form of a complex value, both the x and y values are separately negated:

-z = -x – iy

Given the same complex value in polar (r, q) coordinates, the equivalent negation is accomplished by adding p to the value of q. 

-z = (r, q + p)

CNeg function

// z = -a
FUNCTION CNeg  (CONST a:  TComplex):  TComplex;
BEGIN
  RESULT.form := a.form;
  CASE a.form OF
    cfPolar:
      BEGIN
        RESULT.r := a.r;
        RESULT.theta := FixAngle(a.theta + PI)
      END;
 
    cfRectangular:
      BEGIN
        RESULT.x := -a.x;
        RESULT.y := -a.y
      END;
  END
END {CNeg};

 


Download ComplexMathLibrary


Updated 18 Feb 2002


since 24 June 2001