【问题标题】:How to work with complex numbers in C?如何在 C 中处理复数?
【发布时间】:2011-06-20 23:44:16
【问题描述】:

如何在 C 语言中处理复数?我看到有一个complex.h 头文件,但它并没有给我太多关于如何使用它的信息。如何以有效的方式访问实部和虚部?是否有获取模块和阶段的原生函数?

【问题讨论】:

  • 我使用 C 而不是 C++,因为它更容易绑定到我的 Python 代码。

标签: c complex-numbers


【解决方案1】:

这段代码会对你有所帮助,而且相当不言自明:

#include <stdio.h>      /* Standard Library of Input and Output */
#include <complex.h>    /* Standard Library of Complex Numbers */

int main() {

    double complex z1 = 1.0 + 3.0 * I;
    double complex z2 = 1.0 - 4.0 * I;

    printf("Working with complex numbers:\n\v");

    printf("Starting values: Z1 = %.2f + %.2fi\tZ2 = %.2f %+.2fi\n", creal(z1), cimag(z1), creal(z2), cimag(z2));

    double complex sum = z1 + z2;
    printf("The sum: Z1 + Z2 = %.2f %+.2fi\n", creal(sum), cimag(sum));

    double complex difference = z1 - z2;
    printf("The difference: Z1 - Z2 = %.2f %+.2fi\n", creal(difference), cimag(difference));

    double complex product = z1 * z2;
    printf("The product: Z1 x Z2 = %.2f %+.2fi\n", creal(product), cimag(product));

    double complex quotient = z1 / z2;
    printf("The quotient: Z1 / Z2 = %.2f %+.2fi\n", creal(quotient), cimag(quotient));

    double complex conjugate = conj(z1);
    printf("The conjugate of Z1 = %.2f %+.2fi\n", creal(conjugate), cimag(conjugate));

    return 0;
}

与:

creal(z1): 得到实数部分(float crealf(z1),long double creall(z1)

cimag(z1): 得到虚部(对于 float cimagf(z1),对于 long double cimagl(z1)

处理复数时要记住的另一个重要点是,cos()exp()sqrt() 等函数必须替换为它们的复数形式,例如ccos()cexp()csqrt()

【讨论】:

  • 这是什么double complex?这是语言扩展还是某种宏魔法?
  • @Calmarius complex 是标准的 c99 类型(在 GCC 的底层,它实际上是 _Complex 类型的别名)。
  • @Snaipe: complex 不是一个类型。它是一个扩展为_Complex 的宏,它是一个类型说明符,但它本身不是一个类型。复杂类型为float _Complexdouble _Complexlong double _Complex
  • 不仅仅是 GCC,标准中定义 _Complex 是类型说明符,complex.h 有一个扩展为 _Complex 的复杂宏。 _Bool 和 stdbool.h 也是如此。
【解决方案2】:

自 C99 标准(GCC 的-std=c99 选项)以来,复杂类型在 C 语言中。一些编译器甚至可以在更早的模式下实现复杂类型,但这是非标准和不可移植的扩展(例如 IBM XL、GCC,可能是 intel,...)。

您可以从 http://en.wikipedia.org/wiki/Complex.h 开始 - 它提供了 complex.h 中函数的描述

本手册http://pubs.opengroup.org/onlinepubs/009604499/basedefs/complex.h.html 还提供了一些关于宏的信息。

要声明一个复杂变量,请使用

  double _Complex  a;        // use c* functions without suffix

  float _Complex   b;        // use c*f functions - with f suffix
  long double _Complex c;    // use c*l functions - with l suffix

要将值赋予复数,请使用来自complex.h_Complex_I 宏:

  float _Complex d = 2.0f + 2.0f*_Complex_I;

(实际上,(0,-0i) 数字和 NaN 在单半复数中可能存在一些问题)

模块为cabs(a)/cabsl(c)/cabsf(b);实部是creal(a),虚部是cimag(a)carg(a) 用于复杂的参数。

要直接访问(读/写)真实的图像部分,您可以使用这个 unportable GCC-extension:

 __real__ a = 1.4;
 __imag__ a = 2.0;
 float b = __real__ a;

【讨论】:

  • 几乎每一个复杂的函数都会被编译器高效地实现为内置函数。只需使用现代编译器并给它一些非零级别的优化。
  • 仅供参考,因为 OP 提到了 Python 绑定,所以在使用 Python 时,我尝试坚持使用 C89(因为 Python 的其余代码是 C89,如果您希望扩展在 Windows 上运行,通常是MVSC 编译,仅限于 C89)。我不知道这是绝对必要的。
  • 表达式(complex float) { r, i }也可以用来设置数字的各个部分并且独立(例如允许实部为INF而虚部为NAN)。这避免了 GCC 特定的关键字,尽管我不确定它是否真的是可移植的。
  • 请注意,复杂的支持在 C99 中是可选的:如果编译器定义了 __STDC_NO_COMPLEX__,他们可能根本就没有它。然而,在实践中,它是在主要编译器上实现的。
  • Jasen,查看 N1256 草案 open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf#page=182“7.3 复数算术 ”的第 182 页。可能在 C99 中选择了这样的关键字,以不破坏现有的手动实现复杂的 c (C90) 程序。如果包含 complex 将被定义为宏,扩展为 _Complex。您可能还对 Derek M. Jones 的“新 C 标准:经济和文化评论”(2008 年)第 500 页“复杂类型”people.ece.cornell.edu/land/courses/ece4760/… 感兴趣
【解决方案3】:

为方便起见,可以为类型生成宏包含tgmath.h 库。它为所有类型的变量创建与双重版本相同的函数名称。例如,它定义了一个 sqrt() 宏,该宏扩展为 sqrtf()sqrt()sqrtl() 函数,具体取决于提供的参数类型。

所以不用记住不同类型的变量对应的函数名!

#include <stdio.h>
#include <tgmath.h>//for the type generate macros. 
#include <complex.h>//for easier declare complex variables and complex unit I

int main(void)
{
    double complex z1=1./4.*M_PI+1./4.*M_PI*I;//M_PI is just pi=3.1415...
    double complex z2, z3, z4, z5; 

    z2=exp(z1);
    z3=sin(z1);
    z4=sqrt(z1);
    z5=log(z1);

    printf("exp(z1)=%lf + %lf I\n", creal(z2),cimag(z2));
    printf("sin(z1)=%lf + %lf I\n", creal(z3),cimag(z3));
    printf("sqrt(z1)=%lf + %lf I\n", creal(z4),cimag(z4));
    printf("log(z1)=%lf + %lf I\n", creal(z5),cimag(z5));

    return 0;
}

【讨论】:

    【解决方案4】:

    复数的概念是从计算负二次根的需要引入数学的。复数概念被各种工程领域所采用。

    如今,复数被广泛用于高级工程领域,例如物理学、电子学、力学、天文学等......

    负平方根示例的实部和虚部:

    #include <stdio.h>   
    #include <complex.h>
    
    int main() 
    {
        int negNum;
    
        printf("Calculate negative square roots:\n"
               "Enter negative number:");
    
        scanf("%d", &negNum);
    
        double complex negSqrt = csqrt(negNum);
    
        double pReal = creal(negSqrt);
        double pImag = cimag(negSqrt);
    
        printf("\nReal part %f, imaginary part %f"
               ", for negative square root.(%d)",
               pReal, pImag, negNum);
    
        return 0;
    }
    

    【讨论】:

      【解决方案5】:

      要提取复值表达式z 的实部,请使用符号__real__ z。 同样,使用z 上的__imag__ 属性来提取虚部。

      例如;

      __complex__ float z;
      float r;
      float i;
      r = __real__ z;
      i = __imag__ z;
      

      r 是复数“z”的实部 i 是复数“z”的虚部

      【讨论】:

      • 这些是 gcc 特定的扩展。另一个答案已经提到了它们,accepted answer 已经在标准 C 中做到了这一点。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-04
      • 2016-08-28
      • 2020-12-14
      • 1970-01-01
      • 2013-07-10
      • 2013-03-29
      相关资源
      最近更新 更多