From 2e4a950e610f59c7fe50261ec50293ca938124bb Mon Sep 17 00:00:00 2001 From: Demetrios Agourakis Date: Fri, 30 Oct 2026 10:27:14 -0300 Subject: [PATCH] Preserve the imaginary zero sign in complex log1p translation --- .../xsimd/arch/common/xsimd_common_math.hpp | 2 +- test/test_complex_exponential.cpp | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/include/xsimd/arch/common/xsimd_common_math.hpp b/include/xsimd/arch/common/xsimd_common_math.hpp index ad8a2741a..84a8d72c9 100644 --- a/include/xsimd/arch/common/xsimd_common_math.hpp +++ b/include/xsimd/arch/common/xsimd_common_math.hpp @@ -1873,7 +1873,7 @@ namespace xsimd { using batch_type = batch, A>; using real_batch = typename batch_type::real_batch; - batch_type u = 1 + self; + batch_type u(real_batch(1.) + self.real(), self.imag()); batch_type logu = log(u); return select(u == batch_type(1.), self, diff --git a/test/test_complex_exponential.cpp b/test/test_complex_exponential.cpp index 8dffdf5c4..0373b98a2 100644 --- a/test/test_complex_exponential.cpp +++ b/test/test_complex_exponential.cpp @@ -213,4 +213,52 @@ TEST_CASE_TEMPLATE("[complex exponential]", B, BATCH_COMPLEX_TYPES) Test.test_sign(); } } +#ifndef __FAST_MATH__ +TEST_CASE_TEMPLATE("[complex log1p branch cut]", B, BATCH_COMPLEX_TYPES) +{ + using value_type = typename B::value_type; + using real_type = typename value_type::value_type; + const real_type zero(0); + const auto below = std::nextafter(real_type(-1), real_type(-2)); + const std::array samples = { + value_type(-4, zero), value_type(-4, -zero), + value_type(-2, zero), value_type(-2, -zero), + value_type(below, zero), value_type(below, -zero), + value_type(-2, real_type(0.5)), value_type(-2, real_type(-0.5)), + value_type(real_type(-0.5), real_type(0.25)), value_type(real_type(-0.5), real_type(-0.25)), + value_type(2, 1), value_type(2, -1), + value_type(zero, zero), value_type(zero, -zero) + }; + 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::log1p(B::load_unaligned(inputs.data())).store_unaligned(results.data()); + for (size_t lane = 0; lane < B::size; ++lane) + { + const auto expected = std::log(value_type(real_type(1) + inputs[lane].real(), inputs[lane].imag())); + if (std::isinf(expected.real())) + { + CHECK(std::isinf(results[lane].real())); + CHECK_EQ(std::signbit(results[lane].real()), std::signbit(expected.real())); + } + else + { + 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_EQ(std::signbit(results[lane].imag()), std::signbit(expected.imag())); + if (expected.imag() == zero) + { + CHECK_EQ(results[lane].imag(), zero); + } + } + } +} +#endif #endif