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

Viewed 569

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;
}
3 Answers

How can one do this portably?

It's just... addition:

z = 3.0;
z += I * 2.0;

You could first zero the imaginary part, if you want to:

z = creal(z) + I * 2.0;

how does one assign the real and imaginary parts of a variable individually?

// assign real
z = new_value + I * cimag(z);
// assign imaginary 
z = creal(z) + I * new_value;

One could prefer _Imaginary_I or CMPLXF macros over I.

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;

I found another possible solution. The code below assumes that complex double is bit-equivalent to double[2] (which I think is correct but would appreciate a verification!). I think the solution below may be more efficient since it doesn't involve an extra addition operation for each assignment (as does KamilCuk's solution).

If anyone can verify this is will be portable to all platforms, I would appreciate it.

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

#define SET_REAL(z, x) ( *((double *) &(z)) = (x) )
#define SET_IMAG(z, x) ( *(((double *) &(z)) + 1) = (x) )

int
main()
{
  complex double z;

  SET_REAL(z, 3.0);
  SET_IMAG(z, 2.0);

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

  return 0;
}
Related