如何在C中分配复变量的实部和虚部
Posted
技术标签:
【中文标题】如何在C中分配复变量的实部和虚部【英文标题】:How to assign real and imaginary parts of complex variables in C 【发布时间】:2021-01-17 16:12:01 【问题描述】:使用 C99 标准的复数类型,如何分别分配变量的实部和虚部?我发现 GNU 扩展 __real__ 和 __imag__ 可以做到这一点,如下面的示例程序所示。但这不是便携式或标准的。怎么可以便携呢?
#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;
【问题讨论】:
【参考方案1】:C 2018 6.2.5 13 意味着我们可以将复数视为两个元素的数组:
每个复杂类型都具有与恰好包含对应实数类型的两个元素的数组类型相同的表示和对齐要求;第一个元素等于复数的实部,第二个元素等于复数的虚部。
这是粗略的措辞,与 6.5 7 中明确声明的别名规则不一致,但标准并不完善,关于整数和指针的脚注 41 和 49 表明,关于表示和对齐的此类声明应该允许一些用途尽管有 6.5 7 中的规则,对象的一个视图为另一个视图。如果是这样,我们可以定义宏:
#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])
之后Re(x)
和Im(x)
,当给定一个复杂对象时,为实部或虚部产生一个左值。所以我们可以分配它们:
Re(x) = 3;
Im(x) = 4;
【讨论】:
【参考方案2】:如何便携地做到这一点?
只是……补充:
z = 3.0;
z += I * 2.0;
如果您愿意,可以先将虚部归零:
z = creal(z) + I * 2.0;
如何分配变量的实部和虚部 个人?
// assign real
z = new_value + I * cimag(z);
// assign imaginary
z = creal(z) + I * new_value;
人们可能更喜欢_Imaginary_I
或 CMPLXF
宏而不是I
。
【讨论】:
"可能更喜欢_Imaginary_I
或"...还有什么? ;-)【参考方案3】:
我找到了另一种可能的解决方案。下面的代码假设复数 double 与 double[2] 位等价(我认为这是正确的,但希望得到验证!)。我认为下面的解决方案可能更有效,因为它不涉及每个分配的额外加法操作(就像 KamilCuk 的解决方案一样)。
如果有人能验证这是否可以移植到所有平台,我将不胜感激。
#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;
【讨论】:
这是错误的——它破坏了严格的别名。不,它不是“更有效”,(在我看来,它的效率会更低,因为它可能不允许一些编译器优化)。 Compilers are smart enough 和 C 语言不是 1:1 的汇编。 @KamilCuk:它不会破坏严格的别名,因为 C 2018(及更早版本)6.2.5 13 说“每个复杂类型具有与恰好包含两个元素的数组类型相同的表示和对齐要求对应的实型;第一个元素等于复数的实部,第二个元素等于复数的虚部”,从脚注 41 到前面关于整数的段落我们知道,这种关于表示和对齐的措辞旨在暗示可互换性。 您的宏将更直观地定义为#define Re(x) (((double *) &(x))[0])
和#define Im(x) (((double *) &(x))[1])
,之后您可以简单地编写Re(x) = 3;
和Im(x) = 4;
。
@Eric:谢谢你,我更喜欢你的解决方案。如果你做出回答,我会给你功劳。
这可能需要一个单独的问题,但您认为有一种方法可以定义像您的 Re/Im 这样适用于 double、float 和 long double 的宏吗?就像在宏内部一样,我可以检查输入变量的类型以确定如何转换指针吗?类似于:#if type(z) == float then (((float *) &(x))[0])以上是关于如何在C中分配复变量的实部和虚部的主要内容,如果未能解决你的问题,请参考以下文章
FFTW 和 OpenCV 的 C++ 接口,Mat 输出中的实部和虚部