如何比较两个复数?

时间:2015-12-11 19:21:27

标签: c c99 complex-numbers

在C中,复数是浮点数或双数,并且与规范类型具有相同的问题:

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

int main(void)
{
    double complex a = 0 + I * 0;
    double complex b = 1 + I * 1;

    for (int i = 0; i < 10; i++) {
        a += .1 + I * .1;
    }

    if (a == b) {
        puts("Ok");
    }
    else {
        printf("Fail: %f + i%f != %f + i%f\n", creal(a), cimag(a), creal(b), cimag(b));
    }

    return 0;
}

结果:

$ clang main.c
$ ./a.out 
Fail: 1.000000 + i1.000000 != 1.000000 + i1.000000

我尝试这种语法:

a - b < DBL_EPSILON + I * DBL_EPSILON

但是编译器讨厌它:

main.c:24:15: error: invalid operands to binary expression ('_Complex double' and '_Complex double')
    if (a - b < DBL_EPSILON + I * DBL_EPSILON) {
        ~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

这最后工作正常,但有点挑剔:

fabs(creal(a) - creal(b)) < DBL_EPSILON && fabs(cimag(a) - cimag(b)) < DBL_EPSILON

3 个答案:

答案 0 :(得分:4)

您可以计算复数绝对值(也称为 norm 模数幅度),而不是比较复数组件。他们的差异,即复杂平面上两者之间的距离:

if (cabs(a - b) < DBL_EPSILON) {
    // complex numbers are close
}

即使没有精确问题,小的复数也会接近零,这个问题也存在于实数中。

答案 1 :(得分:3)

比较2个复数浮点数非常类似于比较2个实数浮点数。

比较精确等价通常是不够的,因为涉及的数字包含很小的计算错误。

而不是if (a == b)代码需要if (nearlyequal(a,b))

通常的方法是double diff = cabs(a - b),然后将diffDBL_EPSILON之类的小常量值进行比较。

a,b为大数时,此失败,因为它们之间的差异可能比DBL_EPSILON大许多个数量级,即使a,b仅因其最不重要而有所不同位。

这对于小数字也是失败的,因为a,b之间的差异可能相对较大,但比DBL_EPSILON小许多个数量级,因此当值相对完全不同时返回true

复数在字面上会为问题添加另一个维度问题,因为实部和虚部本身可能会有很大不同。因此nearlyequal(a,b)的最佳答案高度依赖于代码的目标。

为简单起见,让我们使用差异的大小与a,b的平均大小进行比较。控制常量ULP_N近似于允许a,b不同的最不重要的二进制数字的数量。

#define ULP_N 4

bool nearlyequal(complex double a, complex double b) {
  double diff = cabs(a - b);
  double mag = (cabs(a) + cabs(b))/2;
  return diff <= (mag * DBL_EPSILON * (1ull << ULP_N));
}

答案 2 :(得分:1)

由于复数表示为浮点数,因此您必须处理their inherent imprecision。如果浮点数在machine epsilon范围内,则它们“足够接近”。

通常的方法是减去它们,取绝对值,然后看它是否足够接近。

#include <complex.h>
#include <stdbool.h>
#include <float.h>

static inline bool ceq(double complex a, double complex b) {
    return cabs(a-b) < DBL_EPSILON;
}