【问题标题】:C99 complex support with visual studioVisual Studio 的 C99 复杂支持
【发布时间】:2010-11-06 23:56:18
【问题描述】:

我想使用 C99 中定义的复数,但我需要支持不支持它的编译器(想到的是 MS 编译器)。

我不需要很多函数,在没有支持的编译器上实现所需的函数并不太难。但是我很难实现“类型”本身。理想情况下,我想做这样的事情:

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

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

但如果编译器无法识别“浮点数复数”,我不确定我是否知道如何执行此操作。我实际上认为这是不可能的,但Dinkumware 的 C 库似乎另有说明。解决办法是什么 ?我不介意使用函数/宏对类型进行操作,但我需要一种方法来为复数赋值,并以与 C99 兼容的方式取回其实部/虚部。

解决方案

我最终做了这样的事情:

#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

为类型定义,如下定义一组复杂函数:

#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

这有点复杂,但这使我能够在可用时轻松重用 C lib 函数,并且可以通过代码生成器部分自动化。

【问题讨论】:

    标签: c visual-studio c99 complex-numbers


    【解决方案1】:

    无论您做什么,都无法在非 C99 编译器中正确解析“float complex”。因此,与其写那个,不如做一些 typedef。如果您只需要支持一种复杂类型,那会容易得多,所以我将使用float complex 进行演示。

    首先,定义类型:

    #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
    

    然后,我们需要能够创建复数,并模拟 creal 和 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
    

    接下来,编写包含加法、减法、乘法、除法和比较的函数。

    #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.
    

    请注意,上述代码中add_complex(c, 5)在C89模式下不起作用,因为编译器不知道如何将5变成复数。这是一个在没有编译器支持的情况下在 C 中修复的棘手问题——您必须求助于新的 tgmath.h 使用的技巧,这些技巧是特定于编译器的。

    不幸的是,所有这一切的结果是,像 a+b 这样用于添加复数的漂亮 C99 语法必须写成 add_complex(a, b)

    另一种选择(正如另一张海报指出的那样)是在非 C99 编译器上使用 C++ std::complex。如果您可以将内容包装在 typedef 和 #ifdefs 中,这可能没问题。但是,您需要 C++ 或 C99。

    【讨论】:

    • C++ 不起作用,因为在具有 C 链接的函数的签名中不能有 std::complex。我可以为此添加一个包装器,但是,使用 C++ 根本没有什么意义。由于该项目目前不依赖于 C++,这代表了一个很大的依赖关系——我需要支持许多平台,而不仅仅是最近的 gcc 和 MS 编译器。
    • 如果您担心您使用的链接样式,我认为您需要在不同时间编译的目标文件之间具有链接时(二进制)兼容性,而不仅仅是源代码兼容性。在这种情况下,最好始终使用 struct float_complex 方法。否则,有人可能会在编译器的 C89 模式下编译文件,尝试调用在 C99 模式下编译的库中的函数,但会看到奇怪的结果,因为 _Complex 和 float_complex 结构的调用约定不同。
    • 我同意 Doug 的观点:如果你想要 C99/C89 兼容的代码,你不会在你的 float_complex 结构上使用 + 运算符,所以对有两种类型没有兴趣,一种是 C99 复杂,另一种是你内部的复杂结构。
    【解决方案2】:

    我在 msdn 网站上找到了一个库。这是一个链接。 http://msdn.microsoft.com/en-us/library/0352zzhd.aspx

    希望对你有帮助。

    【讨论】:

    • 它是 C++ &lt;complex&gt; 标头。只要问题作者卡在 C 上,他/她就无法正常使用它。
    猜你喜欢
    • 2011-10-05
    • 2017-02-09
    • 2018-07-14
    • 1970-01-01
    • 1970-01-01
    • 2017-10-30
    • 2016-06-05
    • 2017-03-26
    • 1970-01-01
    相关资源
    最近更新 更多