blob: 74d210ede2c06d47058f4f2b4447209652fe840b [file]
// 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>());
}