Search code examples
c++complex-numbers

Avoiding NANs for expressions that are partly imaginary in c++


I have an expression whose outcome is a real number, but is composed of imaginary terms (that cancel one another). A significantly more simple example than the one I am considering would be something like,

z = a + 1/[sqrt(a-b) - a] - f[sqrt(a-b)] = a

where a and b are real numbers and f is some function that statisfies the above expression. It would not surprise you that in some cases, say for b > a (which does not always occur, but could occur in some cases), the above expression returns nan, since some of its terms are imaginary.

Sometimes, it is possible to work out the algebra and write out this not-really-complex expression using real numbers only. However, in my case, the algebra is very messy (so messy that even Matlab's symbolic package and Mathematica are unable to trivially simplify).

I am wondering if there is some other way to work out expressions you know to be real, but are partly imaginary.


PS: not important for this question, but for more info about the expression I am dealing with, please see another question I previously asked.


Solution

  • tl;dr for the comment thread:

    If you know you're doing something that will involve imaginary numbers, just use std::complex.

    You can't avoid getting NaN if you insist on asking for a real result to something (sqrt, say) that you know will have an imaginary component. There is no real answer it can give you.

    At the end of your computation, if imag(result) is zero (or within a suitable epsilon), then your imaginary parts cancelled out and you have a real(result).

    As a concrete example:

    #include <complex>
    #include <iostream>
    
    int main()
    {
        std::complex<double> a{-5, 0}; // -5 + 0i
        std::complex<double> b{ 5, 0}; // +5 + 0i
        auto z = b + sqrt(a) - sqrt(a);
        std::cout  << "z = " << real(z) << " + " << imag(z) << "i\n";
    }
    

    prints

    z = 5 + 0i
    

    With your new example

    z = a + 1/(sqrt(a-b) - a) - f(sqrt(a-b)) = a
    

    it'll be useful to make a of type std::complex in the first place, and to use a complex 1+0i for the numerator as well. This is because of the way overloaded operators are resolved:

    using cx = std::complex<double>;
    
    cx f(cx); // whatever this does, it also needs to handle complex inputs
    
    cx foo(cx a, cx b)
    {
      return a + cx{1}/(sqrt(a-b) - a) - f(sqrt(a-b));
    }
    auto