Division by complex in clang++ versus g++

前端 未结 4 1706
无人及你
无人及你 2021-02-19 16:57

When I compile the following code with g++ (4.8.1 or 4.9.0) or clang++ (3.4) I get different outputs.

#include 
#include 

int mai         


        
相关标签:
4条回答
  • 2021-02-19 17:18

    The standard says in [complex.numbers] (26.4/3):

    If the result of a function is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.

    There are no specifics on how division should be implemented for complex numbers. Only in [complex.member.ops] it says:

    complex<T>& operator/=(const complex<T>& rhs);
    

    Effects: Divides the complex value rhs into the complex value *this and stores the quotient in *this. Returns: *this.

    and in [complex.ops]:

    template<class T> complex<T> operator/(const T& lhs, const complex<T>& rhs);
    

    Returns: complex<T>(lhs) /= rhs.


    As the inverse of 1.e-162 is 1.e+162 and this number is in the range of representable values for a double, the behavior is well defined.

    Thus gcc gets it right and clang has a bug.

    0 讨论(0)
  • 2021-02-19 17:27

    Yes. You should report a bug.

    The complex number 1e-162+0i is identical to the real number 1e-162. This falls well within the range of double. The reciprocal of this value is and should be 1e+162. Any other value represents a bug in the arithmetic libraries.

    The value returned by clang is wrong. It's a bug.

    0 讨论(0)
  • 2021-02-19 17:33

    Update:

    Quoting from the standard:

    If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined. [ Note: most existing implementations of C++ ignore integer overflows. Treatment of division by zero, forming a remainder using a zero divisor, and all floating point exceptions vary among machines, and is usually adjustable by a library function].

    Quoting from http://lists.freebsd.org/pipermail/freebsd-numerics/2014-March/000549.html:

    It appears that clang developers have chosen the naive complex division algorithm.

    ...

    I did a bit of grepping. Could it be that the division algorithm is contained in the file src/contrib/llvm/tools/clang/lib/CodeGen/CGExprComplex.cpp inside the function ComplexExprEmitter::EmitBinDiv ?

    If you look at the code, it certainly looks like it is generating code to perform complex division, and it definitely looks like they are using the naive algorithm.

    Assuming that indeed the clang uses naive complex division the expression 1.0 / c evaluates according to the naive implementation of complex division to the following expression enter image description here,

    enter image description here

    1.e-324 is out of the double range. This results according to the standard to undefined behaviour.

    Also making a search in the LLVM/Clang bug list, it appears that there are quite some issues concerning complex division.

    As such your case is a bug and you should report it.

    For anyone who is interested on how robust complex division is implemented take a look at

    1. http://ideone.com/bqFk8j and
    2. A Robust Complex Division in Scilab.
    0 讨论(0)
  • 2021-02-19 17:46

    Ok so I have this wild guess, that in clang the complex number division is implemented like described on wiki: http://en.wikipedia.org/wiki/Complex_number#Multiplication_and_division.

    One can see that the denominator is in the form c^2 + d^2. So 1.e-162 squared actually falls out of the IEE754 representable range for double which is std::numeric_limits<double>::min() - 2.22507e-308, and we have an underflow.

    gcc somehow works this out, but if clang does simple square, as per @40two's standard quotation it enters into UB, and treats it as 0 after performing 1.e-162^2 + 0.0^2.

    I tested clang and gcc for a number that should not result with underflow when squared.

    #include <iostream>
    #include <complex>
    
    int main() {
      std::complex<double> c = {1.e-104,0};
      std::cout << 1.0/c << std::endl;
      return 0;
    }
    

    Results are fine:

    luk32:~/projects/tests$ g++ --std=c++11 ./complex_div.cpp 
    luk32:~/projects/tests$ ./a.out 
    (1e+104,0)
    luk32:~/projects/tests$ clang++ --std=c++11 ./complex_div.cpp 
    luk32:~/projects/tests$ ./a.out 
    (1e+104,0)
    

    Not sure if this is a bug. But I think this is what is going on.

    Addendum:

    (inf,-nan) is also consistent if one evaluates those expressions by hand

    We get:

    real = (ac+bd) / (o)  - real part 
    imag = (bc-ad) / (o)  - imaginary part
    
    {a,b} = {1.0, 0.0}
    {c,d} = {1.e-104, 0.0}
    
    o is (c^2 + d^2) and we assume it is 0.0.
    
    real / o = (1.e-104 * 1.0 + 0.0 * 0.0) / o = 1/o = inf
    imag / o = (0.0 * 1.e-104 - 1.0 * 0.0) / o = -0.0 / o = -nan
    

    I am just not absolutetly sure about the sign of -0.0 and -nan, I don't know IEE754 enough to evaluate (0.0 * 1.e-104 - 1.0 * 0.0). But everything seems consistent.

    0 讨论(0)
提交回复
热议问题