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

• real numbers, a, b, c, d
• complex numbers, u, v:

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)

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

 // 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};