| 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) |
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) |
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) |
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 |
,
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}; |
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}; |
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}; |
Updated 18 Feb 2002
since 24 June 2001