From 49da11d80a7f0ee29b02dcfe2d158df2e3c9bb64 Mon Sep 17 00:00:00 2001 From: Demetrios Chiuratto Agourakis Date: Tue, 10 Nov 2026 23:28:35 +0000 Subject: [PATCH] Preserve the imaginary zero sign in complex log1p Add 1 to the real part only, so a negative imaginary zero is not flushed before log. --- .../xsimd/arch/common/xsimd_common_math.hpp | 4 ++- test/test_complex_exponential.cpp | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/xsimd/arch/common/xsimd_common_math.hpp b/include/xsimd/arch/common/xsimd_common_math.hpp index 8ab7e0e59..11f0c3770 100644 --- a/include/xsimd/arch/common/xsimd_common_math.hpp +++ b/include/xsimd/arch/common/xsimd_common_math.hpp @@ -1873,7 +1873,9 @@ namespace xsimd { using batch_type = batch, A>; using real_batch = typename batch_type::real_batch; - batch_type u = 1 + self; + // Scalar and complex broadcasts add a positive zero to the imaginary part. + // Translate the real part directly to preserve an imaginary negative zero. + 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..1590409e5 100644 --- a/test/test_complex_exponential.cpp +++ b/test/test_complex_exponential.cpp @@ -151,6 +151,39 @@ struct complex_exponential_test detail::load_batch(ref, expected, i); CHECK_BATCH_EQ(ref, out); } + + using R = real_value_type; + const R zero(0); + const R below = std::nextafter(R(-1), R(-2)); + const R re[] = { -R(4), -R(2), below, -R(0.5), -zero, zero, R(0.5), R(2) }; + const R im[] = { -R(1), -R(0.5), -R(0.25), -zero, zero, R(0.25), R(0.5), R(1) }; + vector_type inputs; + inputs.reserve(64 + size); + for (R real : re) + { + for (R imag : im) + { + inputs.push_back(value_type(real, imag)); + } + } + while (inputs.size() % size != 0) + { + inputs.push_back(inputs.front()); + } + for (size_t i = 0; i < inputs.size(); i += size) + { + batch_type in, out, ref; + detail::load_batch(in, inputs, i); + out = log1p(in); + vector_type scalar(size); + for (size_t lane = 0; lane < size; ++lane) + { + const value_type& z = inputs[i + lane]; + scalar[lane] = std::log(value_type(R(1) + z.real(), z.imag())); + } + detail::load_batch(ref, scalar, 0); + CHECK_BATCH_EQ(ref, out); + } } void test_sign()