4

I need (only) the real part of the product of two complex numbers. Naturally, I can code this as

real(x)*real(y) - imag(x)*imag(y);

or

real(x*y);

The latter, however, formally first computes the complex product, including its imaginary part, and then takes the real part of it. I wonder whether compilers routinely optimize the computation of the imaginary part of the product away or whether I cannot rely on such optimization.

6
  • 2
    You can inspect and compare the generated assembly on Compiler Explorer for various compilers and optimization levels. Have you tried that? Commented Jul 29 at 16:54
  • 6
    the only way to know for sure is write the code and see what assembly gets generated Commented Jul 29 at 16:54
  • godbolt.org/z/n8jn7W578 Commented Jul 29 at 17:20
  • Have a look at godbolt.org/z/6T8K6o4qW, so no it will still call the complex part too. But on the other hand your compiler might even be smarter then you think, if it is a constant calculation the result is stored in your program and the call isn't even made. Commented Jul 29 at 17:25
  • another comparison godbolt.org/z/5nbfnnnxv Commented Jul 29 at 18:11

1 Answer 1

1

(x*y).real() cannot be optimized into the "naive" formula x.real()*y.real() - x.imag()*y.imag() because they do not give equivalent results in all cases.

Although the ISO C++ standard doesn't seem to specify how std::complex behaves for non-numeric values (infinity, NaN, etc), Annex G of the ISO C standard does, and I would expect most C++ implementations to comply with it. Among its rules are:

  • A complex number is an infinity if either part is an infinity (+Inf or -Inf), even if the other is NaN.

  • An infinity multiplied by a finite number (both parts finite) is an infinity.

So for example, if x = (+Inf, NaN) and y = (+1.0, +1.0), then x*y must be an infinity; at least one of its parts must be infinity. But if we used the naive formulas for both the real and imaginary parts of x*y, they would both be NaN. (I suppose this doesn't rule out using the naive formula for the real part only, but it's hard to imagine a practical implementation of complex multiplication that could do this and meet all the constraints.)

Another example would be (+Inf, +Inf) * (+0.0, +1.0). The naive formula would give (NaN, Nan) but the result must be an infinity.

GCC and Clang support the flag -ffinite-math-only, telling them to assume that all floating-point numbers are finite (neither infinity nor NaN). Under this assumption, the naive formula is valid, and if we apply this flag to Pepjin Kramer's example code, you can see that both versions generate identical assembly: https://godbolt.org/z/q3jddhcWv

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.