Introduction
Although the C# language has been used quite successfully in many kinds of projects, it still remains to be proven proficient for scientific computing. Questions remain as to whether or not C# can measure up to the likes of FORTRAN and C++ for scientific and mathematical projects.
Complex Arithmetic
Arithmetic and algebra on complex numbers is important in many fields of science and engineering. Even though C# does not support an intrinsic complex number data type, it doesn't stop you from creating your own. You can do so using a struct
, which has value semantics. The purpose of this article is to present my attempt to fashion a complex arithmetic class for C#.
Using the Code
The struct Complex
is completely contained within the namespace ComplexMath
in the file Complex.cs. There are three separate program files included in the download as well as a documented help file for the project. You can run the program either by using the ComplexMath.dll reference, or by using the ComplexMath
library code directly by simply adding the Complex.cs file to a project. You must, in either case, add the header using ComplexMath
; to the main project file. If you decide to go the DLL route, first build the library (DLL) by running the CompexMath.sln file from which you can generate two ComplexMath.dll files, one each in bin/Release and bin/Debug.
Steps to Use the Library as a DLL
- Start a new C# console application.
- Build both the Debug and Release versions.
- Place the Debug and Release versions of the ComplexMath.dll in their respective bin folders.
- From Project/Add Reference menus, find the ComplexMath.dll for the version (debug or release) and add it to the project.
- Add the header:
using ComplexMath;
to your program file.
- Use the accompanying TestComplexMath.cs file as a prototype for your own program.
Your test program should look something like the one below which briefly demonstrates many of the capabilities of the library:
using System.Collections.Generic;
using System.Text;
using ComplexMath;
namespace TestLib
{
class Program
{
static void Main(string[] args)
{
Complex z1, z2, z3;
z1 = new Complex(1.0, -2.0);
z2 = new Complex(-3.0, -5.0);
z3 = new Complex(0.292342, -0.394875);
Console.WriteLine("instantiated complex numbers:");
Console.WriteLine(string.Format("z1 = {0}", z1));
Console.WriteLine(string.Format("z2 = {0}", z2));
Console.WriteLine(string.Format("z3 = {0}", z3));
z1 = Complex.Random();
z2 = Complex.Random();
z3 = Complex.Random();
Console.WriteLine("random generated:");
Console.WriteLine(string.Format("z1 = {0}", z1));
Console.WriteLine(string.Format("z2 = {0}", z2));
Console.WriteLine(string.Format("z3 = {0}", z3));
Console.WriteLine("basic properties:");
Console.WriteLine(string.Format
("z1.real = {0} z1.imag = {1}", z1.real, z1.imag));
Console.WriteLine(string.Format
("complex conjugate of z1 = {0}.", z1.Conjugate));
Console.WriteLine(string.Format
("complex modulus of z1 = {0}.", z1.Modulus));
Console.WriteLine(string.Format
("complex argument of z1 = {0}.", z1.Argument));
Console.WriteLine("operators:");
z3 = z1 + z2; Console.WriteLine(string.Format("z1 + z2 = {0}", z3));
z3 = z1 - z2; Console.WriteLine(string.Format("z1 - z2 = {0}", z3));
z3 = z1 * z2; Console.WriteLine(string.Format("z1 * z2 = {0}", z3));
z3 = z1 / z2; Console.WriteLine(string.Format("z1 / z2 = {0}", z3));
Console.WriteLine("transcendental functions:");
z3 = Complex.Sin(z1); Console.WriteLine(string.Format("Sin(z1) = {0}", z3));
z3 = Complex.Cos(z1); Console.WriteLine(string.Format("Cos(z1) = {0}", z3));
z3 = Complex.Tan(z1); Console.WriteLine(string.Format("Tan(z1) = {0}", z3));
z3 = Complex.Sinh(z1);
Console.WriteLine(string.Format("Sinh(z1) = {0}", z3));
z3 = Complex.Cosh(z1);
Console.WriteLine(string.Format("Cosh(z1) = {0}", z3));
z3 = Complex.Tanh(z1);
Console.WriteLine(string.Format("Tanh(z1) = {0}", z3));
z3 = Complex.Exp(z1); Console.WriteLine(string.Format("Exp(z1) = {0}", z3));
z3 = Complex.Log(z1); Console.WriteLine(string.Format("Log(z1) = {0}", z3));
z3 = Complex.Log10(z1);
Console.WriteLine(string.Format("Log10(z1) = {0}", z3));
Console.WriteLine("roots:");
z3 = Complex.Sqrt(z1);
Console.WriteLine(string.Format("Sqrt(z1) = {0}", z3));
Console.WriteLine("k 4th roots:");
for (long k = 0; k < 4; k++)
{
z3 = Complex.NthRoot(z1, 4, k);
Console.WriteLine(string.Format("NthRoot(z1, 4, {0}) = {1}",k, z3));
} Console.WriteLine("the five 5th roots of unity:");
Complex z = new Complex(1.0, 0.0);
for (long k = 0; k < 5; k++)
{
z3 = Complex.NthRoot(z, 5, k);
Console.WriteLine
(string.Format("{0}th 5th root of unity = {1}", k, z3));
}
Console.WriteLine("powers:");
int n = 7;
z3 = Complex.Pow(z1, z2);
Console.WriteLine(string.Format("Complex.Pow(z1, z2) = {0}", z3));
z3 = Complex.Pow(z1, n);
Console.WriteLine(string.Format("Complex.Pow(z1, {0}) = {1}", n, z3));
Console.WriteLine("display:");
z1.Show();
z1.ShowPolar();
Console.WriteLine(string.Format("polar form of z1 = {0}.", z1.Polar()));
z1.Dump();
}
}
The accompanying TestLib
program demonstrates all of the capabilities of the Complex struct
. One point worth making is that complex numbers can be expressed both in a rectangular form that most high-school algebra students are familiar with and in the more sophisticated polar form which requires some more advanced understanding of trigonometry, De Moivre's Theorem and Euler's Identity. If you are interested, check them out on the Web.
Note that all of the initialization input assumes that you are using the rectangular form of the complex number, which implies that real is measured along the x-axis of the Argand plane and imaginary is measured along the y-axis of that plane. However, the program has the capacity to output the components of the polar form -- that is, the modulus |z|
and the argument (theta) -- by using the ShowPolar()
and Polar()
methods and the Argument
and Modulus
properties (see above).
Since complex numbers can be added, subtracted, multiplied and divided, it is convenient to have operators to accomplish those ends. Thus, we have incorporated those operators.
History
There appears to be no shortage of postings offering C# code for dealing with complex numbers. These include:
Although I have been involved in scientific mathematical and statistical programming for over 10 years, this article is the first that I have attempted to post. I quickly learned that there are many smarter and better programmers out there who have done a much better job at using C#. For me, this was an excellent learning experience, both in terms of learning some of the pitfalls of scientific computing with C# and having to explain to others what my code does.
Pitfalls
The fact that C# class assignment operators which are automatically generated and cannot be overridden create something of a problem, particularly if one is coming from a C++ background. Once an assignment is made, the assignor and the assignee are locked together by a common pointer. Change one, you change the other. In order to work around that, one would have to implement ICloneable
which can be rather tedious and complicated. For that reason, I chose to use the struct
rather than a class. I am still learning about C#.
I plan to update and improve this code for my own use and for any others who wish to use it, and to do a better job in the future. You may use this code in any manner that you wish. It is always polite and professional to give the original author credit. Please report any comments, criticisms or bugs to the forum below.