Date: Tue, 17 Jun 2025 19:30:37 +0000
The C++ standard, in section [complex.transcendentals], provides the following templates for complex pow:
template<class T> constexpr complex<T> pow(const complex<T>& x, const complex<T>& y);
template<class T> constexpr complex<T> pow(const complex<T>& x, const T& y);
template<class T> constexpr complex<T> pow(const T& x, const complex<T>& y);
Returns: The complex power of base x raised to the yth power, defined as exp(y * log(x)).
See: https://eel.is/c++draft/complex.numbers#complex.transcendentals-20
This is great. Furthermore, I think the additional overloads mentioned in https://eel.is/c++draft/complex.numbers#cmplx.over-3
Function template pow has additional constexpr overloads sufficient to ensure, for a call with one argument of type complex<T1> and the other argument of type T2 or complex<T2>, both arguments are effectively cast to complex<common_type_t<T1, T3>>, where T3 is double if T2 is an integer type and T2 otherwise.
If common_type_t<T1, T3> is not well-formed, then the program is ill-formed.
are understood to be enabled only if T1 is not the same as T2.
Question #1) Is my understanding correct, and if so, would it be a good idea to mention this?
Question #2) Should both arguments be effectively cast to complex<common_type_t<T1, T3>>? I agree that is the right thing to do if the "other" argument type is complex<T2>. However, I think it is better to effectively cast the "other" argument of type T2 to common_type_t<T1, T3> so that the second or third of the first three templates is then used.
Note that the multiplication in "y * log(x)" is complex times complex in the first template, but it is real times complex in the second and third.
Note that real times complex is not only a performance optimization, but it also can give different results in the presence of signed 0.0.
For example, +0.0 + -0.0 = +0.0.
I believe the following sentence from https://en.cppreference.com/w/cpp/numeric/complex/pow, "Non-complex arguments are treated as complex numbers with positive zero imaginary component." is not correct according to the standard.
Furthermore, I see the following results with either the GCC or the LLVM implementations:
x = (16.00, -0.00)
sqrt(x) = (4.00, -0.00)
exp(0.5 * log(x)) = (4.00, -0.00)
pow(x, 0.5) = (4.00, 0.00)
pow(x, 0.5f) = (4.00, 0.00)
where x is std::complex<double>.
I suggest this is a bug and that both pow() tests above should return the complex number (4.00, -0.00).
The code can be found in Compiler Explorer: https://godbolt.org/z/Te8GqhjMj
Regards,
Paul
Received on 2025-06-17 19:30:42