在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
答案 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)
,然后将diff
与DBL_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;
}