From f05f579ac866e96ffd5e069dcec70f211b1f21ab Mon Sep 17 00:00:00 2001 From: Demetrios Agourakis Date: Thu, 29 Oct 2026 23:05:27 -0300 Subject: [PATCH] Select the principal branch of complex acosh in both half-planes --- .../xsimd/arch/common/xsimd_common_trigo.hpp | 3 +- test/test_complex_hyperbolic.cpp | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/include/xsimd/arch/common/xsimd_common_trigo.hpp b/include/xsimd/arch/common/xsimd_common_trigo.hpp index 00ad75076..598c81906 100644 --- a/include/xsimd/arch/common/xsimd_common_trigo.hpp +++ b/include/xsimd/arch/common/xsimd_common_trigo.hpp @@ -80,8 +80,7 @@ namespace xsimd { using batch_type = batch, A>; batch_type w = acos(z); - w = batch_type(-w.imag(), w.real()); - return w; + return batch_type(abs(w.imag()), copysign(w.real(), z.imag())); } // asin diff --git a/test/test_complex_hyperbolic.cpp b/test/test_complex_hyperbolic.cpp index 40e97ca7c..e13d936fc 100644 --- a/test/test_complex_hyperbolic.cpp +++ b/test/test_complex_hyperbolic.cpp @@ -172,4 +172,41 @@ TEST_CASE_TEMPLATE("[complex hyperbolic]", B, BATCH_COMPLEX_TYPES) Test.test_atanh(); } } +TEST_CASE_TEMPLATE("[complex acosh principal branch]", B, BATCH_COMPLEX_TYPES) +{ + using value_type = typename B::value_type; + using real_type = typename value_type::value_type; + const std::array samples = { + value_type(-4, 1), value_type(-4, -1), + value_type(-2, 1), value_type(-2, -1), + value_type(-1, 1), value_type(-1, -1), + value_type(real_type(-0.5), 1), value_type(real_type(-0.5), -1), + value_type(real_type(0.5), 1), value_type(real_type(0.5), -1), + value_type(1, 1), value_type(1, -1), + value_type(2, 1), value_type(2, -1), + value_type(4, 1), value_type(4, -1) + }; + std::array inputs; + std::array results; + const auto tolerance = real_type(64) * std::numeric_limits::epsilon(); + for (size_t start = 0; start < samples.size(); ++start) + { + for (size_t lane = 0; lane < B::size; ++lane) + { + inputs[lane] = samples[(start + lane) % samples.size()]; + } + xsimd::acosh(B::load_unaligned(inputs.data())).store_unaligned(results.data()); + for (size_t lane = 0; lane < B::size; ++lane) + { + const auto expected = std::acosh(inputs[lane]); + CHECK_EQ(results[lane].real(), doctest::Approx(expected.real()).epsilon(tolerance).scale(1)); + CHECK_EQ(results[lane].imag(), doctest::Approx(expected.imag()).epsilon(tolerance).scale(1)); + CHECK_GE(results[lane].real(), real_type(0)); + CHECK_EQ(std::signbit(results[lane].imag()), std::signbit(inputs[lane].imag())); + const auto recovered = std::cosh(results[lane]); + CHECK_EQ(recovered.real(), doctest::Approx(inputs[lane].real()).epsilon(tolerance).scale(1)); + CHECK_EQ(recovered.imag(), doctest::Approx(inputs[lane].imag()).epsilon(tolerance).scale(1)); + } + } +} #endif