ccomplex-numbers

How to assign real and imaginary parts of complex variables in C


Using the complex number type of the C99 standard, how does one assign the real and imaginary parts of a variable individually? I found the GNU extension __real__ and __imag__ will do it, as in the following example program. But this isn't portable or standard. How can one do this portably?

#include <stdio.h>
#include <stdlib.h>
#include <complex.h>

int
main()
{
  complex double z;

  __real__ z = 3.0;
  __imag__ z = 2.0;

  fprintf(stderr, "z = %f + i %f\n", creal(z), cimag(z));

  return 0;
}

Solution

  • C 2018 6.2.5 13 implies we can treat complex number as an array of two elements:

    Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number.

    This is crude wording, at odds with the explicitly stated aliasing rules in 6.5 7, but the standard is imperfect, and footnotes 41 and 49, about integers and pointers, suggest such statements about representation and alignment are supposed to allow some use of one view of the object for another in spite of the rules in 6.5 7. If so, we can define macros:

    #define Re(x)   (_Generic((x), \
        complex float       : ((float *)       &(x)), \
        complex double      : ((double *)      &(x)), \
        complex long double : ((long double *) &(x)))[0])
    
    #define Im(x)   (_Generic((x), \
        complex float       : ((float *)       &(x)), \
        complex double      : ((double *)      &(x)), \
        complex long double : ((long double *) &(x)))[1])
    

    after which Re(x) and Im(x), when given a complex object, produce an lvalue for the real or imaginary part. So we can assign them:

    Re(x) = 3;
    Im(x) = 4;
    

    However, it is safer to take the “same representation” requirement to mean you can rely on the memory layout of the complex type to have the same layout as two real objects which can be aliased through character type:

    complex double c;
    memcpy(&c[0], & (double) {3}, sizeof c[0]);
    memcpy(&c[1], & (double) {2}, sizeof c[1]);
    

    or by using a union:

    union { complex double a; double b[2]; } u = { 1.0 };
    u.b[0] = 3;
    u.b[1] = 2;
    

    With this interpretation, you would have to use the above methods to access the real and imaginary parts of a complex object; there would be no way to access them directly using an lvalue of real type without a union.