如何比较两个复数?
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));
}
在 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));
}