Complex division by zero

An NMath customer submitted the following support question:

I’m working on the primitives (NMathCoreShared.dll) and have found a rather odd ‘quirk’ with complex division by zero:

DoubleComplex aa = new DoubleComplex(0.0, 0.0);
DoubleComplex bb = new DoubleComplex(5.2, -9.1);
DoubleComplex cc = new DoubleComplex();

cc = bb/aa;
Console.WriteLine(cc);  // (NaN,NaN)

double g = -5.0 / 0.0;
Console.WriteLine(g);   // -Infinity

On my machine (Athlon FX), I get the value NaN back when I’m pretty sure it should be ‘Inf’?

The code implementing complex division in the NMath complex number class does not check to see if the divisor is zero. It just applies the formula for complex division in terms of the operands’ real and imaginary parts:

\frac{a + bi}{c + di} = \frac{ac + bd}{c^2 + d^2} + i\frac{bc - ad}{c^2 + d^2}     (1)

As you can see, when c = d = 0 both the real and imaginary components of the quotient take the form 0/0. Now the IEEE standard says that for real numbers a/0, a not equal to 0, should yield +∞ or -∞ where the sign depends on whether a > 0 or a < 0 and 0/0 should yield NaN (Not a Number). So a direct application of the formulas for division of complex number, without “special casing” the zero denominator case, will yield a result of (NaN, NaN).

One might be tempted to say “OK, that’s the end of that. (NaN, NaN) is obviously the correct behavior”. However, this does not set very well with intuition. If I have a fixed complex number w which is divided by another variable complex number z, it seems that as z gets smaller and smaller, the quotient should get bigger and bigger. There is a slight technicality here–there really is no notion of bigger and smaller for the complex number. That is, there is no ordering of the complex numbers that is consistent with its algebraic structure. In particular there is no notion of negative and positive complex numbers, so the concept of positive and negative infinity are not well defined. Let’s take a look at some theoretical stuff in the hopes it will help to guide our thinking.

Let’s start with something more familiar. What does it mean to divide a real number by zero? From a purely algebraic sense if a and b are two real numbers then the number a ÷ b is the solution to the equation b * x = a. When b = 0 this equation has no solution and so the quotient is not defined (unless we extend the real numbers to include infinity and define algebraic operations that include infinity, but let’s not go there now as it isn’t really germane to our conversation). Where does infinity come in? Well, one can also look at the question from an analysis point of view, where one deals in functions and limits. With this perspective, given a real number a one defines a division function fa(x) = a ÷ x. This function is well defined for all x not equal to 0. But, analysis deals also with limits, and one would naturally ask what happens when x gets close to 0. The answer is as x → 0, fa → ∞. Formally, this means the following: Given any real number M>0, there exists a corresponding real number δ > 0 such that |fa | > M whenever |x| < δ. Now, we can apply this definition to complex numbers since we have a notion of absolute value for complex numbers: if z = a + ib is a complex number, then the modulus of z, |z|, is defined as

|z|=\sqrt{a^2 + b^2}

That is it’s the euclidean distance from the point z = (a,b) to the origin in the complex plane. From this it is clear that as a complex number tends to zero, so must its real and imaginary parts. So as the denominator in equation (1) above tends to zero, so do its real and imaginary parts c and d. The fact that c and d are squared in the denominator of the real and imaginary parts of the quotient means that they will tend to zero faster than the their corresponding numerators and hence, using a L’Hôpital’s Rule based argument, or a straight δ, M based argument, we can see that both the real and imaginary parts of the quotient tend to infinity as the denominator tends towards zero. Whether it is positive or negative infinity is another question.

But enough theoretical talk. Let’s look at something concrete. Unlike C#, the standard C++ library includes a complex number class. What do they do when dividing by zero? Here’s the result I get with Microsoft’s C++ complex class:

Microsoft Visual C++ 15.0

(1,1)/(0,0) = (1.#QNAN,1.#QNAN)
(0,0)/(0,0) = (1.#QNAN,1.#QNAN)
1/0 = 1.#INF
0/0 = -1.#IND

Hmm. They seem to just apply the formulas and get 0/0 for the real and imaginary parts. How about the GNU C++ compiler?

g++ 4.3.2

(1,1)/(0,0) = (inf,inf)
(0,0)/(0,0) = (nan,nan)
1/0 = inf
0/0 = nan

Interesting. They return infinite real and imaginary parts. What’s more interesting is that the two compilers do not agree. And just for fun, here’s the result with the complex class included in Microsoft’s F# compiler:

F#

(1,1)/(0,0) = NaNr+NaNi
(0,0)/(0,0) = NaNr+NaNi
1/0 = Infinity
0/0 = NaN

No surprise here. At least Microsoft is consistent.

What does the GNU compiler do about positive and negative infinity in its results?

(1,1)/(0,0) = (inf,inf)
(-1,-1)/(0,0) = (-inf,-inf)
(1,-1)/(0,0) = (inf,-inf)
(-1,1)/(0,0) = (-inf,inf)

Apparently, they have adopted the convention that the sign of infinity in the real and imaginary parts of the quotient is the same as the signs of the real and imaginary parts of the numerator, respectively.

So, what should NMath do? On the one hand .NET is a Microsofty kind of thing, so maybe we should be consistent with their C++ implementation. On the other hand g++ is a widely distributed compiler and they seem to take the moral high road here. What would you do?

Steve

One thought on “Complex division by zero

  1. A quick test in maple and matlab produces the following:

    Maple 11:
    >> a := 0.0+0.0*I; -> 0. + 0. I
    >> b := 3.0+4.0*I; -> 3.0 + 4.0 I
    >> b/a; -> Float(infinity) – Float(infinity) I

    Matlab R2008b:
    >> a = complex(0.0,0.0);
    >> b = complex(3.0,4.0);
    >> b/a -> Inf + Infi

    Which is pretty conclusive and could be proven with an alternative form of the complex division that is muxed together from real + imaginary division components to account for this case.

    But, ignoring the mathematical bounds of the problem

Leave a Reply

Your email address will not be published. Required fields are marked *

Top