5

In C, complex numbers are float or double and have same problem as canonical types:

#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;
}

The result:

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

I try this syntax:

a - b < DBL_EPSILON + I * DBL_EPSILON

But the compiler hate it:

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

This last works fine but it’s a little fastidious:

fabs(creal(a) - creal(b)) < DBL_EPSILON && fabs(cimag(a) - cimag(b)) < DBL_EPSILON
Sanpi
  • 420
  • 7
  • 18
  • Don't you need to take the absolute value of `creal(a) - creal(b)`? Ditto `cimag..`? – Shannon Severance Dec 11 '15 at 19:31
  • Surely the only way to compare two complex numbers is to compare their magnitude, modulus. – Weather Vane Dec 11 '15 at 19:32
  • ...and comparing for equality will suffer the same problem as any other floating point comparisons, please see [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) – Weather Vane Dec 11 '15 at 19:36
  • Also, try to add some parentheses: `if ((a - b) < (DBL_EPSILON + I * DBL_EPSILON)) {`. The error is telling you that the operator is expecting only two operands, and you're accidentally providing more. The parentheses should clear that up. – skrrgwasme Dec 11 '15 at 19:41
  • 1
    @skrrgwasme: http://port70.net/~nsz/c/c11/n1570.html#7.3 – too honest for this site Dec 11 '15 at 19:45
  • @Olaf My bad. I was just being a dummy and reading too fast. I assume you pointed me to that reference because I mentioned the `double complex` declaration in my previous comment. I see now that the OP did declare his/her variables that way. I could swear they weren't there when I left that comment... – skrrgwasme Dec 11 '15 at 19:48
  • @skrrgwasme: Read it and keep the link. It is the C standard, so yes, it is the only autoritative reference (no matter it actually is the final draft - the official release is not free of charge). – too honest for this site Dec 11 '15 at 19:51
  • @Olaf I agree that's it's an authoritative reference. I guess I just don't understand why you linked me to it. I assumed it was because I mistakenly mentioned the `double complex` declaration, but your last comment confused me a bit. Is there something else I said incorrectly that you were trying to help me understand? – skrrgwasme Dec 11 '15 at 19:55
  • @skrrgwasme: just read 7.3.1p4 – too honest for this site Dec 11 '15 at 19:56
  • @WeatherVane There are numerous ways to compare any two anythings, including complex numbers. Testing whether two complex numbers are centered in overlapping squares of some size is not inherently worse than testing whether they are centered in overlapping discs; it depends on the intended use. – Kaz Dec 11 '15 at 20:01
  • @Kaz OP's `main()` code is specifically testing for equality, as shown by the accepted answer. – Weather Vane Dec 11 '15 at 20:04
  • @Sanpi please never edit your question to reflect solutions. Your question and following comments and answers will make no sense, unless readers delve back into the edit history. Please undo your edit, unless it was a genuine oversight in the question. – Weather Vane Dec 11 '15 at 20:09
  • @WeatherVane I would add `fabs()`. I also add `cabs()` but it’s a mistake, I reverted it. Sorry. – Sanpi Dec 11 '15 at 20:16

3 Answers3

5

Comparing 2 complex floating point numbers is much like comparing 2 real floating point numbers.

Comparing for exact equivalences often is insufficient as the numbers involved contain small computational errors.

So rather than if (a == b) code needs to be if (nearlyequal(a,b))


The usual approach is double diff = cabs(a - b) and then comparing diff to some small constant value like DBL_EPSILON.

This fails when a,b are large numbers as their difference may many orders of magnitude larger than DBL_EPSILON, even though a,b differ only by their least significant bit.

This fails for small numbers too as the difference between a,b may be relatively great, but many orders of magnitude smaller than DBL_EPSILON and so return true when the value are relatively quite different.

Complex numbers literally add another dimensional problem to the issue as the real and imaginary components themselves may be greatly different. Thus the best answer for nearlyequal(a,b) is highly dependent on the code's goals.


For simplicity, let us use the magnitude of the difference as compared to the average magnitude of a,b. A control constant ULP_N approximates the number of binary digits of least significance that a,b are allowed to differ.

#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));
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 1
    +1 for a thorough explanation. If you use `1u` instead of `1` to gain one more bit of range for the allowed difference, maybe you should instead use `1LL`. – chqrlie Dec 12 '15 at 11:35
4

Instead of comparing the complex number components, you can compute the complex absolute value (also known as norm, modulus or magnitude) of their difference, which is the distance between the two on the complex plane:

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

Small complex numbers will appear to be close to zero even if there is no precision issue, a separate issue that is also present for real numbers.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • There is no modulus in your answer. it also makes no sense on non-integers, as it is the remainder of an integer division. Maybe a false friend of your native language? – too honest for this site Dec 11 '15 at 19:59
  • 1
    @Olaf: C11 7.3.8.1: *The cabs functions compute the complex absolute value (also called norm, modulus, or magnitude) of z*. `modulus` is the correct mathematical term for this regarding complex numbers. – chqrlie Dec 11 '15 at 20:01
  • 2
    @chqrlie [You are technically correct, ***the best kind of correct***](https://www.youtube.com/watch?v=hou0lU8WMgo), but also confusing which isn't so correct on a site about learning. There's already something else better understood as "modulus". Just say "absolute value". – Schwern Dec 11 '15 at 20:05
  • Hmmm.. interesting I know that term just from the remainder-thing. Sorry then. – too honest for this site Dec 11 '15 at 20:05
  • 1
    @Schwern: OK, it is ambiguous in English, it must be my French: `modulo` for remainder and `module` for complex magnitude. Albeit the function name is `cabs(z)` and the mathematical syntax for this is `|z|`, I find it even more confusing describe the result as *absolute value* for a complex number. At least write *complex absolute value*. I added some extra teachings in the answer. – chqrlie Dec 11 '15 at 20:11
1

Since complex numbers are represented as floating point numbers, you have to deal with their inherent imprecision. Floating point numbers are "close enough" if they're within the machine epsilon.

The usual way is to subtract them, take the absolute value, and see if it's close enough.

#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;
}
Schwern
  • 153,029
  • 25
  • 195
  • 336
  • 4
    Why bother with a ternary operator? – chqrlie Dec 11 '15 at 19:56
  • `(return cabs(a-b) < DBL_EPSILON ? true : false) ? true : false` to be safe – djechlin Dec 11 '15 at 20:00
  • @chqrlie A habit I picked up from other languages that don't have a boolean type. I suppose it's not necessary here. – Schwern Dec 11 '15 at 20:03
  • Interesting... I cannot think of another language where is would be necessary. Not java or C++ for sure, but in C you can add emphasis this way: `return !!(cabs(a-b) < DBL_EPSILON);` – chqrlie Dec 11 '15 at 20:05
  • @chqrlie Programmers in typeless languages like Perl have a bad tendency to return an integer value (`return result`) when they really mean a boolean (`return result ? 1 : 0`) leading people to use that undocumented integer. As for C, I only recently discovered that a lot of comparison macros actually return an integer because they're really `a - b`, so I got suspect about `<`. I've seen the `!!` operator before in Perl, but I it's a ["secret operator"](https://metacpan.org/pod/perlsecret) and I generally avoid it for readability purposes. In C, the extra layer of parens makes it even worse. – Schwern Dec 11 '15 at 20:23
  • @Schwern: the `!!` example was just a tease. Even for languages with dynamic typing, `a < b` is usually a boolean, so the extra layer is useless. I guess some people might see it as more readable. The compiler will generate the same code anyway. – chqrlie Dec 11 '15 at 20:35