如何比较两个复数?

How to compare two complex numbers?

在 C 中,复数是 float 或 double,与规范类型有同样的问题:

#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

由于复数表示为浮点数,因此您必须处理 their inherent imprecision. Floating point numbers are "close enough" if they're within the 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;
}

您可以计算复数绝对值(也称为 范数模数magnitude)它们的差值,也就是两者在复平面上的距离:

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

即使没有精度问题,小的复数也会看起来接近于零,这是实数也存在的一个单独问题。

比较 2 个复数浮点数与比较 2 个实数浮点数非常相似。

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

所以代码需要 if (nearlyequal(a,b))

而不是 if (a == b)

通常的方法是 double diff = cabs(a - b),然后将 diff 与一些较小的常数值进行比较,例如 DBL_EPSILON

a,b 是大数时,此 失败 ,因为它们的差异可能比 DBL_EPSILON 大许多数量级,即使 a,b 不同仅由他们的最低有效位。

这对于小数字也失败了,因为 a,b 之间的差异可能相对较大,但比 DBL_EPSILON 小很多数量级,因此 return 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));
}