Document: WG14 N1431


Creation of complex value


Submitter: Fred J. Tydeman (USA)
Submission Date: 2009-12-01
Related documents: N818, SC22WG14.8195, N1399, N1419
Subject: Creation of complex value

Problem: (x + y*I) will NOT do the right thing if "I" is complex and "y" is NaN or infinity. It does work fine if "I" is imaginary. Users and library implementors have noticed this deficiency in the standard and have been surprised that there is no easy to use portable way to create a complex number that can be used in both assignment and static initialization.

WG14 paper N818 presented more details on why the problem exists as well as many possible solutions. Paper N1419 added a couple more possible solutions.

Since those papers, another solution has been found that uses existing C99 features. It uses a union, type punning (footnote 87 in N1401 in 6.5.2.3 Structure and union members, paragraph 3), the required representation and alignment of complex (as per 6.2.5 Types, paragraph 13), and 6.5.2.5 Compound literals. The downside of this usage is a union must be visible where ever this idea is used, and an unnamed object is created (so, cannot be used for initialization of static objects), rather than just two assignments being done.

Proposal number one.

Add the following example to 6.5.2.5 Compound literals or to 6.5.2.3 Structure and union members

EXAMPLE nn: A non-static complex object may be initialized from two real values via type punning:

#include  <math.h>
  union fc2 {           /* 6.5.2.3, paragraph 3, type punning */
    float f[2];         /* 6.2.5, paragraph 13, layout of complex */
    float _Complex fc;
  };
  float _Complex fc0 = (union fc2){INFINITY, NAN}.fc;
  float _Complex fc1 = (union fc2){.f[0]=-0.f, .f[1]=1.f}.fc;

defines and initializes fc0 with the value /math infinity symbol/ + iNAN, fc1 with the value -0.f + i, Note: This usage does not allow for static initialization.

Proposal number two.

Thanks to Steve Adamczyk of EDG for the initial wording and for assurance that there is no implementation difficulty with doing this.

Since the rules for 6.5.2.5 compound literals are defined in terms of the rules in 6.7.8 initialization, both uses would fall out.

After paragraph 11 of 6.7.8 Initialization, add a new paragraph:

If the initializer for an object of complex type is brace-enclosed, the initializer list shall contain either a single expression, in which case the initialization is as described above for scalar types, or two expressions [footnote], in which case the first expression (after conversion to real type) shall initialize the real part of the complex value, and the second expression (after conversion to real type) shall initialize the imaginary part of the complex value.

Footnote: The "ambiguous" case (double complex[2]){x,y} is treated as (double complex[2]){{x,0},{y,0}}, not (double complex[2]){{x,y},{0,0}}.

Add a new example to 6.7.8 Initialization:

EXAMPLE nn: The declarations:

#include <complex.h>
  #include <math.h>
  double complex dc0 = { 3 + 4*I };        /* one complex scalar value */
  double complex dc1 = { INFINITY, NAN };  /* pair of real values */
  double complex dc2 = { 1+2*I, 3+4*I };   /* as if {1, 3} or 1+3*I */
  double complex dc3[] = {1, 2*I, 3+4*I, {5}, {6*I}, {7+8*I}, {9,10}};
  /* as if: { {1,0}, {0,2}, {3,4}, {5,0}, {0,6}, {7,8}, {9,10} } */

show the use of one expression versus two expressions inside braces to initialize complex objects.

Proposal number three.

This has been shipping for several years from HP.

Add 3 new function-like macros to <complex.h> in section 7.3.9 Manipulation functions:

7.3.9.x The CMPLX macros

Synopsis

#include <complex.h>
  double      complex CMPLX( double x, double y ); 
  float       complex CMPLXF( float x, float y );
  long double complex CMPLXL( long double x, long double y ); 

Description

The function-like macros CMPLX(x,y), CMPLXF(x,y), and CMPLXL(x,y) each expands to an expression of the specified complex type, with real part having the value of x (converted) and imaginary part having the value of y (converted). Each macro can be used for static initialization if and only if both x and y could be used as static initializers for the corresponding real type.

The macros act as if an implementation supports imaginary and the macros were defined as:

#define CMPLX(x,y) ((double)(x)+_Imaginary_I*(double)(y))
#define CMPLXF(x,y) ((float)(x)+_Imaginary_I*(float)(y))
#define CMPLXL(x,y) ((long double)(x)+_Imaginary_I*(long double)(y))

Returns

The CMPLX macros return the complex value x + i*y created from a pair of real values, x and y.

Add to the rationale in the section on complex:

x + yI will not create the expected value x + iy if I is complex and "y" is a NaN or an infinity; however, the expected value will be created if I is imaginary. Because of this, the notation {x,y} and/or CMPLX(x,y) as an initializer of a complex object was added to C1x to allow a way to create a complex number from a pair of real values.