Complex Numbers
A complex number has the form a + ib, where a and b are its real and imaginary components and i2 = -1.
Numerics.NET represents a complex number with the immutable Complex<T> structure. The type parameter specifies the real and imaginary component type; the operations available depend on the arithmetic that type supports.
Constructing complex numbers
The Complex<T> structure has two constructors.
The first constructor takes two arguments: the real and imaginary parts of the complex number.
var z1 = new Complex<double>(3.0, 4.0);The second constructor takes one real argument. It constructs a complex number whose imaginary part is zero.
var z2 = new Complex<double>(3.0);Complex numbers can also be created from the polar form: the static FromPolar method takes two arguments: the magnitude and phase of the complex number.
var z3 = Complex<double>.FromPolar(3.0, Constants.Pi / 6.0);Finally, the static RootOfUnity method constructs a complex number that is one of the solutions to the equation xn = 1 . The solutions to this equation are n complex numbers spaced equally on the unit circle. The first parameter of the method is the degree, n, of the root. The second parameter is the index of the root in the series, counting counter-clockwise on the unit circle. An index of 0 corresponds to the root x = 1.
Complex constants
The complex structure provides several read-only fields for commonly used and special complex numbers. These are listed in the following table:
Field | Description |
|---|---|
The imaginary unit i, whose square is -1. | |
Complex zero: 0 + 0i. | |
Complex one: 1 + 0i. | |
Complex infinity. | |
Complex Not-a-Number. |
The Infinity field is a canonical infinity with both components positive infinity when the component type supports IEEE special values. Other infinite values are also supported, including a signed infinite component paired with a finite component or NaN. For example, an infinite real part and a zero imaginary part represent a direction along the real axis.
Direct comparison with Infinity is not recommended. Instead, use the static IsInfinity method.
The NaN field represents complex Not-a-Number. Both components are NaN for IEEE component types. Undefined operations can also produce a NaN in just one component. The static IsNaN method checks whether either component is NaN. Use this predicate rather than equality with the NaN constant: the equality operator returns false when a compared component is NaN. A value with an infinite component and a NaN component satisfies both the infinity and NaN predicates; arithmetic treats the infinite component according to the operation's special-value rules.
Working with complex numbers
The Re property gets the real part of the complex number, while Im gets the imaginary part. The Magnitude property returns the magnitude (absolute value or modulus), the square root of the sum of the squared components. The Phase property returns the phase (argument) of the complex number: the angle between the positive real axis and a line from the origin to the complex number, measured counter-clockwise.
The Magnitude and Phase properties compute their values on each access. If you use these values multiple times, you should consider caching them in a variable.
Details of complex arithmetic
When performing binary operations, if one of the operands is a complex number, the other operand must be a complex number of the same component type, or a real value convertible to that component type. The real operand has a zero imaginary component. Arithmetic returns a complex value with the same component type. Exceptions to this are methods that return a real property of a number: Magnitude, Phase, MagnitudeSquared and Abs. These return the component type T, which is Double only when the complex value has double-precision components.
For IEEE floating-point components, special values follow the complex-arithmetic rules of C23 Annex G. These rules distinguish signed zeros, infinities and NaNs in the individual components. They do not impose IEEE special values on integer component types.
IEEE floating-point arithmetic represents exceptional results in the components rather than throwing an arithmetic exception:
- Underflow affects each component separately and can produce a subnormal value or a signed zero.
- Overflow can make one or both components infinite. An overflowing magnitude does not by itself make finite components infinite.
- An undefined component can be NaN while another component is zero, finite or infinite. Results need not equal the canonical NaN or Infinity constants.
- A NaN input does not require both output components to be NaN. For example, the principal square root of a value with an infinite imaginary component has a positive infinite real component even if the input real component is NaN.
Arithmetic operations
Overloaded operators for all basic arithmetic operators on complex numbers are available. Most contain overloads for cases where one of the operands is real. Corresponding static methods are provided for languages that don't support operator overloading.
The following examples perform the same arithmetic in each language:
var a = new Complex<double>(1, 2);
var b = new Complex<double>(-3, 4);
var c = 2 - 1 / (a + b);Specific to complex numbers is the static Conjugate(Complex<T>) method. This method returns the conjugate of a complex number, leaving the original unchanged.
Operator | Static method equivalent | Description |
|---|---|---|
+z | Returns the complex number z. | |
-z | Returns the negation of the complex number z. | |
z1 + z2 | Adds the complex numbers z1 and z2. | |
z + a | Adds the complex number z and the real number a. | |
a + z | Adds the real number a to the complex number z. | |
z++ | Increments the complex number z by one. | |
z1 - z2 | Subtracts the complex numbers z1 and z2. | |
z - a | Subtracts the real number a from the complex number z. | |
a - z | Subtracts the complex number z from the real number a. | |
z-- | Decrements the complex number z by one. | |
z1 * z2 | Multiplies the complex numbers z1 and z2. | |
z * a | Multiplies the complex number z and the real number a. | |
a * z | Multiplies the real number a and the complex number z. | |
z1 / z2 | Divides the complex number z1 by z2. | |
z / a | Divides the complex number z by the real number a. | |
a / z | Divides the real number a by the complex number z. | |
(no operator) | Returns the complex conjugate of the complex number z. |
There is one other static method that does not have an operator equivalent. The ConjugateMultiply method returns the product of the conjugate of the first argument and the second argument.
Because the complex numbers don't have a natural ordering, only equality and inequality operators are available. No other comparison operators are available.
Functions of complex numbers
The Complex<T> type defines static methods for the most common mathematical functions of complex numbers, including: logarithmic, exponential, trigonometric and hyperbolic functions.
Some functions of real numbers have a limited domain when the result is restricted to be real, but are defined for all real numbers if the result can be complex. Examples of this are the inverse sine and cosine, which are real only for arguments between -1 and +1. The Complex<T> type defines methods that extend these functions to return a complex result.
Multivalued functions return their documented principal branches. On a branch cut along an axis, the sign of the zero component selects the limiting value on that side of the cut. Real-valued overload arguments are treated as having a positive imaginary zero. Negating a complex argument also negates its zero component; negating only the real part can select a different side of a cut.
For example, in (real, imaginary) notation, Sqrt(Complex<T>) maps (-4, +0) to (+0, +2) and (-4, -0) to (+0, -2). Conjugating the input conjugates the result. Treating both input zeros as interchangeable loses this branch information.
The tables below summarize these methods, and their meaning. Special functions with complex argument may be available from the Special class.
Method | Description |
|---|---|
The number E raised to the complex power z. | |
The unit-circle value with phase 2π k/n, where n is the degree and k is the root index. | |
The principal square root of the complex number z. | |
The principal square root of the real number a, which may be negative. | |
The complex number z1 raised to the complex power z2. | |
The complex number z raised to the real power a. | |
The complex number z raised to the integer power n. | |
Natural logarithm of the complex number z. | |
Logarithm of the first argument in the base supplied by the second argument. | |
Base 10 (common) logarithm of the complex number z. |
Method | Description |
|---|---|
Sine of the complex number z. | |
Cosine of the complex number z. | |
Tangent of the complex number z. | |
Inverse sine of the complex number z. | |
Inverse sine of the real number a. | |
Inverse cosine of the complex number z. | |
Inverse cosine of the real number a. | |
Inverse tangent of the complex number z. |
Method | Description |
|---|---|
Hyperbolic sine of the complex number z. | |
Hyperbolic cosine of the complex number z. | |
Hyperbolic tangent of the complex number z. | |
Inverse hyperbolic sine of the complex number z. | |
Inverse hyperbolic cosine of the complex number z. | |
Inverse hyperbolic tangent of the complex number z. |
Differences from System.Numerics.Complex<T>
Complex<T> intentionally differs from some expectations in Microsoft's generic complex reference tests. The comparison below refers to the Microsoft source revision used for the ported special-value tests. It is not a claim about every version of the Microsoft implementation. The numerical policies described here apply to IEEE floating-point component types.
Where C23 Annex G permits either sign of an exceptional zero component, Numerics.NET chooses signs that preserve evenness, conjugation and the rotation identities between trigonometric and hyperbolic functions. These choices follow Kahan's principles of retaining branch information and treating special cases carefully. The additional conventions that use a NaN's stored sign are Numerics.NET representation choices; they do not give NaN a mathematical direction or promise the sign or payload of a NaN result.
In this section, pairs are written as (real, imaginary), and +0 and -0 distinguish the two floating-point zero representations. These pairs describe components, not a text format accepted by the parser.
Exceptional cosine zero signs
Let s(a) be one carrying the stored sign of component a, including a NaN's sign bit. For Cos(Complex<T>) with nonfinite real component x and zero imaginary component y, the imaginary result is the signed zero -s(x)y. For Cosh(Complex<T>), it is s(x)y when x is nonfinite and y is zero, and xs(y) when x is zero and y is nonfinite.
Operation and input | Numerics.NET result | Microsoft reference expectation |
|---|---|---|
Cos(Complex<T>) of (-Infinity, +0) | (NaN, +0) | (NaN, -0) |
Cos(Complex<T>) of (-Infinity, -0) | (NaN, -0) | (NaN, +0) |
Cosh(Complex<T>) of (-0, -Infinity) | (NaN, +0) | (NaN, -0) |
Cosh(Complex<T>) of (+0, -Infinity) | (NaN, -0) | (NaN, +0) |
Both signs are permitted by Annex G in these examples. The difference concerns only the otherwise unspecified zero sign; the required NaN, infinity and finite classifications are unchanged.
Reciprocal of zero
Reciprocal(Complex<T>) is consistent with dividing one by its argument. For complex zero, its real result is infinity carrying the real input zero's sign, and its imaginary result is NaN. The imaginary input zero's sign does not change this result. Microsoft's reference tests instead expect complex zero for all four signed-zero inputs.
Input | Numerics.NET result | Microsoft reference expectation |
|---|---|---|
(+0, +0) or (+0, -0) | (+Infinity, NaN) | (+0, +0) |
(-0, +0) or (-0, -0) | (-Infinity, NaN) | (+0, +0) |
The zero-to-zero rule is a Microsoft compatibility convention. Numerics.NET retains division-consistent behavior at this singularity.
Powers and exponential/logarithm constructions
Pow(Complex<T>, Complex<T>) uses integer-power semantics for finite, exactly integral real exponents. Squaring preserves exact real-component cancellation and structural zeros when a literal exponential/logarithm construction would introduce overflow or NaNs. Other real exponents with nonnegative real bases use real-power semantics.
Base | Exponent | Numerics.NET result |
|---|---|---|
(+Infinity, +0) | (2, +0) or (+Infinity, +0) | (+Infinity, +0) |
(-Infinity, 1) | (2, +0) | (+Infinity, -Infinity) |
(+0, +Infinity) | (2, +0) | (-Infinity, +0) |
(+Infinity, +Infinity) | (2, +0) | (+0, +Infinity) |
(m, m), where m is the largest finite component value | (2, +0) | (+0, +Infinity) |
The ported Microsoft tests include a requirement to match Exp(exponent * Log(value)) component by component. Numerics.NET does not impose that construction on these special cases. Annex G permits power implementations to handle special cases more carefully. A difference from the construction check is not, by itself, a separately measured difference from Microsoft's power implementation.
Formatting and parsing
ToString(String, IFormatProvider) uses algebraic notation, such as 1.5 - 2.5i, rather than Microsoft's generic complex pair notation. The format string and provider control the formatting of each component. When the imaginary component is zero, only the real component is written.
The current parsing APIs do not accept this algebraic presentation format, so a formatting/parsing round trip is not guaranteed. The supported text-format and round-trip contract remains under review. This is a separate limitation from the numerical policies above; it does not establish support for Microsoft's input grammar. Serialize the real and imaginary components separately when round-trip persistence is required.
References
Complex numbers of arbitrary type
The Complex<T> structure is generic over the type of the real numbers. This makes it possible to create complex numbers of different precisions, or even complex integers. All that is needed is to specify the desired real type in the constructor:
var q1 = new Complex<Quad>(1.0, Quad.Sqrt(3));
var pi = 3 * q1.Phase;