Not So Complex Numbers in C#
Written by Mike James   
Thursday, 25 November 2021
Article Index
Not So Complex Numbers in C#
Complex Functions

Complex functions

As well as creating Complex objects using the Cartesian form you can also use the static FromPolarCoodinates method:

z3 = Complex.FromPolarCoordinates(1,Math.PI/2);

As well as complex addition you can use subtraction, multiplication, division and unary negation. The operators also work with mixed real and complex values.

For example you can write:

int t = 2;
z = z*t + t;

The inequality operator is also overloaded allowing you to compare two complex values.

Of course the greater than less than operators are not defined because the concept of greater than/less than makes no sense for complex numbers.

All of the overloaded operators are also available as static methods.

Notice that we can convert between polar and Cartesian form immediately because of the Magnitude and Phase properties. Of course these are read-only as already mentioned.

The fact that we have the usual arithmetic operations is all very well but to do anything useful with complex number we also need some higher operations and functions. In particular we need a power operator. It is one of the peculiarities of C# that it doesn't support a raise to the power operator.

Where other languages would write X^2 or X**2 C# uses Math.Pow(X,2), which to any mathematically trained person is barbaric.

There are arguments for why C# does it this way - but none justify the loss of a power operator.To make things worse the ^ operator is defined as XOR which makes it possible to translate a formula incorrectly but not realise what the problem is until much time has been wasted.

The Complex type simply adds its own version of Pow.

If you write Complex.Pow(Z,X) then it will compute Z^X where X is a double. So to square Z you would write


In most cases it would be better to write Z*Z or Z*Z*Z rather than use the Pow method. As these forms are much more efficient than a general power calculation which generally involves logs and inverse logs doing things this way is no bad thing.

There is also a doubly complex Pow method Pow(Z1,Z2) where both are complex and this computes Z1Z2. Don't use it if Z2 is in fact real because it's not very fast.

Once you have got over the shock of the way that you calculate a power the rest of the Complex functions look reasonable.

There is a conjugate method for example but no unary conjugate operator and a Reciprocal method that works out 1/z - hopefully faster and more accurately than doing the division. All of the complex trig and hyperbolic functions are defined. Inverse trig functions are provided but not inverse hyperbolic. Natural log, log to any base and log to base 10 are provided as is Exp. Other functions can generally be made up using these as the building blocks.

At this point what could be more appropriate than to program the most incredible formula in the mathematical world. As is well known (I hope)

  eiPi  =-1

Thus relating together the numbers e, i, Pi and -1. 

In C# this becomes:

Complex z = Complex.Exp(Complex.ImaginaryOne * Math.PI);

The result printed is:

(-1, 1.22460635382238E-16)

which isn't quite (-1,0) and this should also give you some idea of the (in)accuracy involved in using complex Exp.




One last topic - formatting complex numbers.

The ToString method has a number of overloaded versions that can be used to apply a custom format. As we have already seen the simple ToString() method returns a string formatted as (real,imaginary).

The ToString(string) method will apply a standard format string to the two double precision values that make up the number. You can look up "Standard Numeric Format Strings" in the documentation.  For example:

Complex z = Complex.Exp(Complex.ImaginaryOne * Math.PI);


(-1.000, 0.000)

That is two fixed point values with 3 digits after the decimal point. You can only use format specifiers that apply to Double.

The methods ToString(IFormatProvider) and ToString(string,IFormatProvider) will format the number using the culture specified by the IFormatProvider and in the second case will also format the doubles using the format string.

For example:

Console.WriteLine(z.ToString( new CultureInfo("es-ES")));
Console.WriteLine(z.ToString("F3",new CultureInfo("es-ES")));


(-1, 1,22460635382238E-16)
(-1,000, 0,000)

Note: es-ES is the specifier for Spanish and Spain.

The documentation contains an example of building a custom formatter that can be used to print values in the form a+ib or a+jb. However because you can't inherit from a struct  and even adding extension methods is difficult trying to extend Complex in any reasonably object-oriented way is doomed to fail.



If you would like to see an example of Complex in use then see:

Mandelbrot Zoomer in WPF


  • Mike James, Founder and Chief Editor of I Programmer is a prolific author. In Deep C#: Dive Into Modern C#, published in September 2021, he provides a “deep dive” into various topics that are important or central to the language. By exploring the motivation behind these key concepts, which is so often ignored in the documentation, the intention is to be thought-provoking and to give developers confidence to exploit C#’s wide range of features.



Related Articles

Mandelbrot Zoomer in WPF

The Programmer's Guide to Fractals       

How To Draw Einstein's Face Parametrically       

Good Math       

MathJS A Math Library For JavaScript      


To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.





or email your comment to:


Deep C# - Interface

Interfaces - what are they for? Not quite inheritance yet they seem to fit the same purpose. Find out in this extract from my new book, Deep C#: Dive Into Modern C#.

Deep C# - Inheritance

Inheritance - is it where it all goes wrong? Find out in this extract from my new book, Deep C#: Dive Into Modern C#.

Other Articles





Last Updated ( Thursday, 25 November 2021 )