complex extension to C

David C. Knaak uunet!juniper.cray.com!knaak
Wed Jan 1 09:43:28 PST 1992


At the January 1992 NCEG meeting, I will present the next revision of the
complex extension proposal.  Here are my arguments for the changes I have
made.  I am sending them now for those who want to think about them before
the meeting.


Introduction
------------

At the September 1991 NCEG meeting, the suggestion was made to add an
imaginary data type to the complex extension for C.  Tom MacDonald and I
tried to make this work but couldn't.  The reason is based on the difficulty
in handling infinite values within complex numbers.  As best I can
understand it, the possibility of infinity as either the real or imaginary
part of a complex number is the only motivation behind needing an imaginary
data type.  I contend that this is both unnecessary and unwise.

The first part of this paper discusses this issue.  The second part of this
paper is the revised proposal (revision 7) for the complex extension to C.


Basic goal for complex data type in C
-------------------------------------

Let me first step back and look at the motivation for and the basic goals
for adding complex data type to C.  This extension is to provide as direct
mapping as possible of complex mathematics (complex algebra or complex
analysis) onto the C language.  This mapping should be as close as possible
in both how it is expressed and the results that are obtained.  Because of
the inherent characteristics of the C language and of digital computer
systems, this mapping may be less than perfect but should still be the main
guiding principle.


What does an infinity in a complex number mean?
-----------------------------------------------

 From the point of view of the complex extension supporting the IEEE
standard for binary floating-point arithmetic, I don't think that 
handling infinities in complex numbers is necessary.
Section 6.2 of the standard states: 
   `Signaling NaNs afford values for initialized variables and 
   arithmetic-like enhancements (such as complex-affine infinities 
   or extremely wide range) that are not the subject of the standard.'
If complex-affine infinities are not the subject of the IEEE standard then
supporting complex-affine infinities in the C language should not be 
justified by the standard. 

But more importantly, I fail to see a mathematical justification for
infinite values in complex numbers.  How do you compute the magnitude or
angle of (1.0,infinity)?  Just what does it mean?  I contend that carrying
an infinite complex value through a calculation will yield a result that is
mathematically incorrect or meaningless.

The complex extension to C is explicitly cartesian (otherwise the definition
would be too vague to be useful).  So while an infinite magnitude with polar
coordinates would be easy to image, infinite values in cartesian coordinates
is not.  The meaningful domain is all finite values of the real and
imaginary parts.  Any values outside this domain should be considered
overflow.

If we don't have to deal with preserving infinities in complex numbers, then
there is no longer a need for an imaginary data type (see below) and no
longer a need for the CMPLX macros (see below).

Thus, as far as I can see, all the confusion and complexity results from
trying to handle infinities in complex numbers.  I see this as unnecessary
(because it has no clear meaning) and undesirable (because it adds too much
confusion and complexity to C).  If anyone can demonstrate that infinities
in complex numbers are necessary for mathematicians, then I am willing to
add the complexity.  Otherwise, I am not.


New conversion rules
--------------------

This revision of this paper makes a small change to the conversion rules
(see section 3.2.1.5 of the revised proposal) that eliminates the coercion 
of a real operand to a complex type when the other operand is a complex type.
The key sentence was:
   `The purpose of the conversions is to yield a common type for the 
   two operands, which is also the type of the result.'
and is now:
   `The purpose of the conversions is to yield a common format and 
   length for the two operands and to yield the type of the result.'
Example:
   complex float z = 3.0fi;
   double d = 2.0;
In the expression (z + d), z would be promoted to complex double but d would
not be promoted and the result would be of type complex double.

This change is desirable for several reasons.  It eliminates the problem of
introducing a zero that might yield the wrong sign of one of the parts of
the result.  It eliminates the optimization problem of introducing extra
(unnecessary) terms into the calculation.  And it matches the mathematics,
which does not require any promotion of operands from real to complex.


No need for imaginary type
--------------------------

Does adding an imaginary type have value besides as a way to deal with
infinities and signed zeros in complex numbers?  I think not.  Again, if we
look at the mathematics, the set of real numbers for a algebraic field and
the set of complex numbers form an algebraic field but the set of imaginary
numbers does not.  For example, the multiplication of any 2 members of the
set of imaginary numbers is a real number and therefore not in the set.  In
mathematics, the only added element in going from real to complex numbers is
the square root of -1, usually represented by i or j.  While the set of
purely imaginary numbers is an interesting subset of the complex numbers,
there is no need to invent rules for this subset.  If an imaginary type were
added, don't we need float imaginary, double imaginary, and long double
imaginary?  Having just imaginary constants as part of complex numbers is
sufficient, simple, and just like mathematics.

The syntax for complex numbers in the C language is slightly more awkward
than the syntax of mathematics.  For example, the mathematical expression
   x+yi
is represented in extended C as
   x+y*1.0i
But this additional awkwardness is not limited to complex numbers.  For 
example, the mathematical expression
   c(a+b) = ca+cb
can at best be represented in C as
   c*(a+b) == c*a+c*b

So adding an imaginary type adds a lot of complexity to the language but
is not necessary to express complex mathematics.


No need for CMPLX macros or complex operator
--------------------------------------------

The CMPLX, CMPLXF, and CMPLXL macros were introduced so that each
implementation could promote reals to complex or not promote as appropriate
for that architecture.  With the new promotion rules, these macros are no
longer needed.  Nor is there a need for a complex operator.  There is no
complex operator in mathematics.  Creation of a complex number is done
simply by using i in an expression.

As a convenience, is
   CMPLX(x,y)
easier to type than 
   x+y*1.0i ?  
Is it closer to the mathematical notation? I think not.


No relational operators with complex type
-----------------------------------------

It has also been suggested that complex operands be allowed for the
relational operators.  Since this is not allowed in mathematics, why should
it be allowed in C?  Why impose an arbitrary meaning?  If a user wants to
design an ordering for complex values, he/she can write a macro or function,
for example:
   #define C_LE(z1,z2) (cabs(z1) <= cabs(z2))

And a related issue, I propose that when comparing for equality, if a NaN
appears in either operand, then they compare not equal.


A few more minor changes
------------------------

I few minor changes were made that that don't seem to be controversial.
With imaginary constants, the i, F, L can occur in any order.  In the
absence of the <complex.h> header, 1i is to be interpreted as an imaginary
constant since doing so is consistent with making complex a data type in C
and does not break ANSI conforming programs.


Complex math functions
----------------------

In the last revision of this paper, several aspects of the complex math
functions were left unspecified.  I believe that leaving the return values
"undefined" or "implementation defined" usually doesn't help the
implementors and never helps the users.  This is an attempt at being
explicit, even if it is somewhat arbitrary, on the return values of complex
math functions.  However, I profess an incomplete understanding of this area
of mathematics.

Unless otherwise specified, the domain and range for all complex math
functions is the finite complex plane with the real and imaginary parts x
and y such that
   |x| <= DBL_MAX, |y| <= DBL_MAX.
Since these functions are defined to take double complex arguments,
arguments that are float complex or long double complex need to be promoted
or demoted to double complex.  Any argument to a complex function that is
outside this finite complex plane is an input (domain) error.  Any return
from a complex function that would yield a result outside this finite
complex plane is an output (range) error.  Thus, Nans and infinities as
inputs to or outputs of these functions are not allowed.  An implementation
may set errno but is not required to.

Whenever there are multiple mathematical values for a complex function,
(multi-valued complex functions) we shall define the return value of the
function as the principal value of the function.  If additional rules need
to be specified, they will be.  Here are some specified return values.

For csqrt(z), the return value shall be the principal value with the real
part greater than or equal to zero.  If the real part of the result is zero,
then the imaginary part is greater than or equal to zero.

For clog(z), the return value shall be the principal value with the
imaginary part w of the result in the range -pi < w <= pi.  The imaginary
part of the result is pi only when the real part of the argument is less
than zero and the imaginary part of the argument is zero.

For cpow(0.0,0.0) the return value shall be 1.0 + 0.0i.



David Knaak
knaakacray.com




More information about the Numeric-interest mailing list