| // SPDX-FileCopyrightText: The Eigen Authors |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| #include "main.h" |
| #include "CustomComplex.h" |
| #include "fp_control.h" |
| |
| template <typename T> |
| void check_root_component(T actual, T expected) { |
| if ((numext::isnan)(expected)) { |
| VERIFY((numext::isnan)(actual)); |
| } else if ((numext::isinf)(expected) || numext::is_exactly_zero(expected)) { |
| VERIFY_IS_EQUAL(actual, expected); |
| VERIFY_IS_EQUAL(std::signbit(actual), std::signbit(expected)); |
| } else { |
| VERIFY((numext::isfinite)(actual)); |
| // Two square roots, divisions and scaling: allow a few rounding errors per component. |
| const T bound = |
| T(4) * NumTraits<T>::epsilon() * numext::abs(expected) + T(2) * std::numeric_limits<T>::denorm_min(); |
| VERIFY(numext::abs(actual - expected) <= bound); |
| } |
| } |
| |
| template <typename T> |
| void check_complex_roots(const std::complex<T>& input, const std::complex<T>& root, |
| const std::complex<T>& reciprocal_root) { |
| const auto actual = numext::sqrt(input); |
| const auto reciprocal_actual = numext::rsqrt(input); |
| check_root_component(actual.real(), root.real()); |
| check_root_component(actual.imag(), root.imag()); |
| check_root_component(reciprocal_actual.real(), reciprocal_root.real()); |
| check_root_component(reciprocal_actual.imag(), reciprocal_root.imag()); |
| |
| const auto custom = numext::sqrt(CustomComplex<T>(input.real(), input.imag())); |
| check_root_component(numext::real(custom), root.real()); |
| check_root_component(numext::imag(custom), root.imag()); |
| } |
| |
| template <typename T> |
| void complex_roots_special_values() { |
| using C = std::complex<T>; |
| const T zero = T(0); |
| const T inf = NumTraits<T>::infinity(); |
| const T nan = NumTraits<T>::quiet_NaN(); |
| for (T sign : {T(-1), T(1)}) { |
| const T y = sign * zero; |
| check_complex_roots(C(zero, y), C(zero, y), C(inf, nan)); |
| check_complex_roots(C(-zero, y), C(zero, y), C(inf, nan)); |
| check_complex_roots(C(T(4), y), C(T(2), y), C(T(0.5), -y)); |
| check_complex_roots(C(T(-4), y), C(zero, sign * T(2)), C(zero, -sign * T(0.5))); |
| for (T x : {zero, T(1), T(-1), inf, -inf, nan}) { |
| check_complex_roots(C(x, sign * inf), C(inf, sign * inf), C(zero, -y)); |
| } |
| for (T magnitude : {zero, T(1)}) { |
| check_complex_roots(C(inf, sign * magnitude), C(inf, y), C(zero, -y)); |
| check_complex_roots(C(-inf, sign * magnitude), C(zero, sign * inf), C(zero, -y)); |
| } |
| } |
| for (T value : {zero, T(1), T(-1), nan}) { |
| check_complex_roots(C(value, nan), C(nan, nan), C(nan, nan)); |
| check_complex_roots(C(nan, value), C(nan, nan), C(nan, nan)); |
| } |
| const auto positive_inf_nan = numext::sqrt(C(inf, nan)); |
| const auto negative_inf_nan = numext::sqrt(C(-inf, nan)); |
| check_root_component(positive_inf_nan.real(), inf); |
| VERIFY((numext::isnan)(positive_inf_nan.imag())); |
| VERIFY((numext::isnan)(negative_inf_nan.real())); |
| VERIFY((numext::isinf)(negative_inf_nan.imag())); |
| } |
| |
| template <typename T> |
| void complex_roots_finite() { |
| using C = std::complex<T>; |
| const bool flushes_inputs = ScopedFlushToZero::hardwareFlushesSubnormalInputs(); |
| const int minimum_exponent = std::numeric_limits<T>::min_exponent - std::numeric_limits<T>::digits; |
| // sqrt((3 + 4i) * 2^(2k)) = (2 + i) * 2^k, exactly. |
| for (int exponent = minimum_exponent; exponent <= std::numeric_limits<T>::max_exponent - 3; ++exponent) { |
| if (exponent % 2 != 0 || (flushes_inputs && exponent < std::numeric_limits<T>::min_exponent - 1)) continue; |
| const T scale = std::ldexp(T(1), exponent); |
| const T root_scale = std::ldexp(T(1), exponent / 2); |
| for (T sign_x : {T(-1), T(1)}) { |
| for (T sign_y : {T(-1), T(1)}) { |
| const T u = sign_x > 0 ? T(2) : T(1); |
| const T v = sign_x > 0 ? T(1) : T(2); |
| check_complex_roots(C(sign_x * T(3) * scale, sign_y * T(4) * scale), C(u * root_scale, sign_y * v * root_scale), |
| C((u / T(5)) / root_scale, (-sign_y * v / T(5)) / root_scale)); |
| } |
| } |
| } |
| |
| const T minimum = (std::numeric_limits<T>::min)(); |
| const T maximum = NumTraits<T>::highest(); |
| const T lower = T(2) * minimum; |
| const T upper = maximum / T(4); |
| const T values[] = {std::numeric_limits<T>::denorm_min(), |
| minimum, |
| std::nextafter(lower, T(0)), |
| lower, |
| std::nextafter(lower, maximum), |
| std::nextafter(upper, T(0)), |
| upper, |
| std::nextafter(upper, maximum), |
| maximum}; |
| // MPFR, 256 bits: components of sqrt(1+i) and 1/sqrt(1+i). |
| const T a = T(1.0986841134678099660398011952406783785L); |
| const T b = T(0.4550898605622273413043577578224685696L); |
| const T c = T(0.7768869870150186536720794765315734741L); |
| const T d = T(0.3217971264527913123677217187091049045L); |
| for (T p : values) { |
| if (p == T(0) || (flushes_inputs && p < minimum)) continue; |
| const T root_p = numext::sqrt(p); |
| for (T sign : {T(-1), T(1)}) { |
| check_complex_roots(C(p, sign * p), C(a * root_p, sign * b * root_p), C(c / root_p, -sign * d / root_p)); |
| check_complex_roots(C(-p, sign * p), C(b * root_p, sign * a * root_p), C(d / root_p, -sign * c / root_p)); |
| const T half_root = root_p * T(0.7071067811865475244008443621048490393L); |
| const T inverse_half_root = T(0.7071067811865475244008443621048490393L) / root_p; |
| check_complex_roots(C(T(0), sign * p), C(half_root, sign * half_root), |
| C(inverse_half_root, -sign * inverse_half_root)); |
| } |
| } |
| |
| const T large = std::ldexp(T(1), std::numeric_limits<T>::max_exponent - 2); |
| const T small = std::ldexp(T(1), -std::numeric_limits<T>::max_exponent / 3); |
| const T root_large = numext::sqrt(large); |
| // small/large underflows, but the smaller component of sqrt(large + i*small) does not. |
| check_complex_roots(C(large, small), C(root_large, small / (T(2) * root_large)), C(T(1) / root_large, -T(0))); |
| check_complex_roots(C(-large, -small), C(small / (T(2) * root_large), -root_large), C(T(0), T(1) / root_large)); |
| |
| // A strided input selects scalar evaluation even on backends with complex packets. |
| Array<C, 34, 1> storage; |
| Map<Array<C, 17, 1>, Unaligned, InnerStride<2>> input(storage.data()); |
| for (Index i = 0; i < input.size(); ++i) { |
| const int exponent = i % 2 ? std::numeric_limits<T>::max_exponent - 4 : std::numeric_limits<T>::min_exponent; |
| const T scale = std::ldexp(T(1), exponent - exponent % 2); |
| input(i) = C(T(3) * scale, T(4) * scale); |
| } |
| const Array<C, 17, 1> roots = input.sqrt(); |
| const Array<C, 17, 1> reciprocal_roots = input.rsqrt(); |
| for (Index i = 0; i < input.size(); ++i) { |
| const T scale = numext::sqrt(input(i).imag() / T(4)); |
| check_root_component(roots(i).real(), T(2) * scale); |
| check_root_component(roots(i).imag(), scale); |
| check_root_component(reciprocal_roots(i).real(), (T(2) / T(5)) / scale); |
| check_root_component(reciprocal_roots(i).imag(), (T(-1) / T(5)) / scale); |
| } |
| } |
| |
| EIGEN_DECLARE_TEST(complex_sqrt) { |
| CALL_SUBTEST(complex_roots_special_values<float>()); |
| CALL_SUBTEST(complex_roots_special_values<double>()); |
| CALL_SUBTEST(complex_roots_special_values<long double>()); |
| CALL_SUBTEST(complex_roots_finite<float>()); |
| CALL_SUBTEST(complex_roots_finite<double>()); |
| CALL_SUBTEST(complex_roots_finite<long double>()); |
| } |