C99 complex support with visual studio

David Cournapeau picture David Cournapeau · Jun 30, 2009 · Viewed 8.6k times · Source

I would like to use complex numbers as defined in C99, but I need to support compilers which do not support it (MS compilers come to mind).

I don't need many functions, and implementing the needed functions on compilers without support is not too difficult. But I have a hard time implementing the 'type' itself. Ideally, I would like to do something like:

#ifndef HAVE_CREAL
double creal(complex z)
{
/* .... */
}
#endif

#ifndef HAVE_CREALF
float creal(float complex z)
{
/* ... */
}
#endif

But I am not sure I see how to do this if the compiler cannot recognize 'float complex'. I would actually think it is impossible, but the C library by Dinkumware seems to indicate otherwise. What is the solution ? I don't mind using functions/macros for operations on the type, but I need a way to assign values to a complex number, and get back its real/imaginary part in a way whichi is compatible with C99.

Solution

I ended up doing something like this:

#ifdef USE_C99_COMPLEX
#include <complex.h>
typedef complex my_complex;
#else
typedef struct {
  double x, y;
} my_complex;
#endif

/*
 * Those unions are used to convert a pointer of my_complex to native C99
 * complex or our own complex type indenpendently on whether C99 complex
 * support is available
 */
#ifdef USE_C99_COMPLEX
typedef union {
    my_complex my_z;
    complex c99_z;
} __complex_to_c99_cast;
#else
typedef union {
    my_complex my_z;
    my_complex c99_z;
} __complex_to_c99_cast;
#endif

for the type definition, and as follows to define a set of complex functions:

#ifndef HAVE_CREAL
double my_creal(my_complex z)
{
    union {
            my_complex z;
            double a[2];
    } z1;
    z1.z = z;
    return z1.a[0];
}
#endif

#ifdef HAVE_CREAL
my_complex my_creal(ny_complex z)
{
    __complex_to_c99_cast z1;
    __complex_to_c99_cast ret;

    z1.my_z = z;
    ret.c99_z = creal(z1.c99_z);
    return ret.npy_z;
}
#endif

That's a bit complicated, but this enables me to easily reuse C lib functions when available, and it can be partly automated through code generator.

Answer

Doug picture Doug · Jun 30, 2009

No matter what you do, you can't make "float complex" parse properly in a non-C99 compiler. So instead of writing that, make some typedefs. It's far easier if you only have to support one complex type, so I'll just demonstrate with float complex.

First, define the types:

#if __STDC_VERSION__ >= 199901L
//using a C99 compiler
#include &lt;complex.h>
typedef float _Complex float_complex;
#else
typedef struct 
{
    float re, im;
} float_complex;
#endif

Then, we need to be able to create complex numbers, and emulate creal and cimag.

#if __STDC_VERSION__ >= 199901L
//creal, cimag already defined in complex.h

inline complex_float make_complex_float(float real, float imag)
{
   return real + imag * I;
}
#else
#define creal(z) ((z).re)
#define cimag(z) ((z).im)

extern const complex_float complex_i; //put in a translation unit somewhere
#define I complex_i
inline complex_float make_complex_float(float real, float imag)
{
    complex_float z = {real, imag};
    return z;
}
#endif

Next, write functions that wrap addition, subtraction, multiplication, division, and comparisons.

#if __STDC_VERSION__ >= 199901L
#define add_complex(a, b) ((a)+(b))
//similarly for other operations
#else //not C99
inline float_complex add_complex(float_complex a, float_complex b)
{
  float_complex z = {a.re + b.re, a.im + b.im};
  return z;
}
//similarly for subtract, multiply, divide, and comparison operations.

Note that add_complex(c, 5) doesn't work in C89 mode in the above code, because the compiler doesn't know how to make 5 into a complex. This is a tricky problem to fix in C without compiler support -- you have to resort to tricks like the new tgmath.h uses, which are compiler-specific.

Unfortunately, the effect of all of this is that the nice C99 syntax like a+b to add complex numbers has to be written add_complex(a, b).

Another option (as another poster pointed to) is to use C++ std::complex on non-C99 compilers. This might be OK if you can wrap things in typedefs and #ifdefs. However, you'd require either C++ or C99.