[libcxx-commits] [libcxx] [libc++] Use libm implementations in exp for complex numbers (PR #165457)
Aleksei Nurmukhametov via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 29 06:21:04 PDT 2025
https://github.com/nurmukhametov updated https://github.com/llvm/llvm-project/pull/165457
>From 11c9295ceb1da6e18df4324f85a3e5417bc83625 Mon Sep 17 00:00:00 2001
From: Aleksei Nurmukhametov <anurmukh at amd.com>
Date: Tue, 28 Oct 2025 18:18:51 +0000
Subject: [PATCH 1/3] [libc++] Use libm implementations in exp for complex
numbers
Replace the custom implementation with calls to compiler builtins, which
delegate to libm implementations. This also improves accuracy, as
demonstrated by the added test.
---
libcxx/include/complex | 21 +++-------
.../numerics/complex.number/exp.pass.cpp | 40 +++++++++++++++++++
2 files changed, 45 insertions(+), 16 deletions(-)
create mode 100644 libcxx/test/libcxx/numerics/complex.number/exp.pass.cpp
diff --git a/libcxx/include/complex b/libcxx/include/complex
index d8ec3d95c10ed..0ae2087d492f2 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -1074,24 +1074,13 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> sqrt(const complex<_Tp>& __x) {
// exp
+_LIBCPP_HIDE_FROM_ABI inline _Complex float __cexp(_Complex float __v) { return __builtin_cexpf(__v); }
+_LIBCPP_HIDE_FROM_ABI inline _Complex double __cexp(_Complex double __v) { return __builtin_cexp(__v); }
+_LIBCPP_HIDE_FROM_ABI inline _Complex long double __cexp(_Complex long double __v) { return __builtin_cexpl(__v); }
+
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
- _Tp __i = __x.imag();
- if (__i == 0) {
- return complex<_Tp>(std::exp(__x.real()), std::copysign(_Tp(0), __x.imag()));
- }
- if (std::isinf(__x.real())) {
- if (__x.real() < _Tp(0)) {
- if (!std::isfinite(__i))
- __i = _Tp(1);
- } else if (__i == 0 || !std::isfinite(__i)) {
- if (std::isinf(__i))
- __i = _Tp(NAN);
- return complex<_Tp>(__x.real(), __i);
- }
- }
- _Tp __e = std::exp(__x.real());
- return complex<_Tp>(__e * std::cos(__i), __e * std::sin(__i));
+ return complex<_Tp>(__from_builtin_tag(), std::__cexp(__x.__builtin()));
}
// pow
diff --git a/libcxx/test/libcxx/numerics/complex.number/exp.pass.cpp b/libcxx/test/libcxx/numerics/complex.number/exp.pass.cpp
new file mode 100644
index 0000000000000..ccf03cccd0a35
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/complex.number/exp.pass.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <complex>
+
+// template<class T>
+// complex<T>
+// exp(const complex<T>& x);
+
+#include <complex>
+#include <cassert>
+#include <cmath>
+
+#include "test_macros.h"
+
+template <class T>
+void test_overflow_case() {
+ typedef std::complex<T> C;
+
+ // In this case, the overflow of exp(real_part) is compensated when
+ // sin(imag_part) is close to zero, resulting in a finite imaginary part.
+ C z(T(90.0238094), T(5.900613e-39));
+ C result = std::exp(z);
+
+ assert(std::isinf(result.real()));
+ assert(result.real() > 0);
+
+ assert(std::isfinite(result.imag()));
+ assert(std::abs(result.imag() - T(7.3746)) < T(1.0));
+}
+
+int main(int, char**) {
+ test_overflow_case<float>();
+ return 0;
+}
>From bda7bcef9dfe334ab09a5ca33f9685e2858533e1 Mon Sep 17 00:00:00 2001
From: Aleksei Nurmukhametov <anurmukh at amd.com>
Date: Wed, 29 Oct 2025 13:06:25 +0000
Subject: [PATCH 2/3] WIP: preserve original implementation in case when libm
can't provide cexp functions
---
libcxx/CMakeLists.txt | 1 +
libcxx/cmake/config-ix.cmake | 25 +++++++++++++++++++++++++
libcxx/include/__config_site.in | 1 +
libcxx/include/complex | 26 ++++++++++++++++++++++++++
4 files changed, 53 insertions(+)
diff --git a/libcxx/CMakeLists.txt b/libcxx/CMakeLists.txt
index a119850cd808e..9ab717d99be2b 100644
--- a/libcxx/CMakeLists.txt
+++ b/libcxx/CMakeLists.txt
@@ -749,6 +749,7 @@ config_define(${LIBCXX_ENABLE_UNICODE} _LIBCPP_HAS_UNICODE)
config_define(${LIBCXX_ENABLE_WIDE_CHARACTERS} _LIBCPP_HAS_WIDE_CHARACTERS)
config_define(${LIBCXX_ENABLE_TIME_ZONE_DATABASE} _LIBCPP_HAS_TIME_ZONE_DATABASE)
config_define(${LIBCXX_ENABLE_VENDOR_AVAILABILITY_ANNOTATIONS} _LIBCPP_HAS_VENDOR_AVAILABILITY_ANNOTATIONS)
+config_define_if(C_SUPPORTS_C99_COMPLEX _LIBCPP_HAS_C99_COMPLEX)
# TODO: Remove in LLVM 21. We're leaving an error to make this fail explicitly.
if (LIBCXX_ENABLE_ASSERTIONS)
diff --git a/libcxx/cmake/config-ix.cmake b/libcxx/cmake/config-ix.cmake
index 270d80575adcf..32e98fba49b19 100644
--- a/libcxx/cmake/config-ix.cmake
+++ b/libcxx/cmake/config-ix.cmake
@@ -103,6 +103,31 @@ endif()
check_symbol_exists(__PICOLIBC__ "string.h" PICOLIBC)
+# Check for the existence of <complex.h> complex math functions.
+# This is necessary even though libcxx uses the builtin versions
+# of these functions, because if the builtin cannot be used, a reference
+# to the library function is emitted. Using compiler builtins for these
+# functions requires corresponding C99 library functions to be present.
+
+cmake_push_check_state()
+list(APPEND CMAKE_REQUIRED_LIBRARIES m)
+check_c_source_compiles("
+#include <complex.h>
+typedef __complex__ float float_type;
+typedef __complex__ double double_type;
+typedef __complex__ long double ld_type;
+volatile float_type tmpf;
+volatile double_type tmpd;
+volatile ld_type tmpld;
+int main(void) {
+ tmpf = cexpf(tmpf);
+ tmpd = cexp(tmpd);
+ tmpld = cexpl(tmpld);
+ return 0;
+}
+" C_SUPPORTS_C99_COMPLEX)
+cmake_pop_check_state()
+
# Check libraries
if(WIN32 AND NOT MINGW)
# TODO(compnerd) do we want to support an emulation layer that allows for the
diff --git a/libcxx/include/__config_site.in b/libcxx/include/__config_site.in
index b68c0c8258366..2161015e81dd0 100644
--- a/libcxx/include/__config_site.in
+++ b/libcxx/include/__config_site.in
@@ -32,6 +32,7 @@
#cmakedefine01 _LIBCPP_HAS_WIDE_CHARACTERS
#cmakedefine01 _LIBCPP_HAS_TIME_ZONE_DATABASE
#cmakedefine01 _LIBCPP_INSTRUMENTED_WITH_ASAN
+#cmakedefine01 _LIBCPP_HAS_C99_COMPLEX
// PSTL backends
#cmakedefine _LIBCPP_PSTL_BACKEND_SERIAL
diff --git a/libcxx/include/complex b/libcxx/include/complex
index 0ae2087d492f2..21518b0e5ddb6 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -1074,6 +1074,7 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> sqrt(const complex<_Tp>& __x) {
// exp
+#if _LIBCPP_HAS_C99_COMPLEX
_LIBCPP_HIDE_FROM_ABI inline _Complex float __cexp(_Complex float __v) { return __builtin_cexpf(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex double __cexp(_Complex double __v) { return __builtin_cexp(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex long double __cexp(_Complex long double __v) { return __builtin_cexpl(__v); }
@@ -1082,6 +1083,31 @@ template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
return complex<_Tp>(__from_builtin_tag(), std::__cexp(__x.__builtin()));
}
+#else
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
+ _Tp __i = __x.imag();
+ if (__i == 0) {
+ return complex<_Tp>(std::exp(__x.real()), std::copysign(_Tp(0), __x.imag()));
+ }
+ if (std::isinf(__x.real())) {
+ if (__x.real() < _Tp(0)) {
+ if (!std::isfinite(__i))
+ __i = _Tp(1);
+ } else if (__i == 0 || !std::isfinite(__i)) {
+ if (std::isinf(__i))
+ __i = _Tp(NAN);
+ return complex<_Tp>(__x.real(), __i);
+ }
+ }
+ if (std::isinf(__e)) {
+ _Tp __e2 = std::exp(__x.real() * _Tp(0.5));
+ return complex<_Tp>(__e2 * std::cos(__i) * __e2, __e2 * std::sin(__i) * __e2);
+ }
+ _Tp __e = std::exp(__x.real());
+ return complex<_Tp>(__e * std::cos(__i), __e * std::sin(__i));
+}
+#endif
// pow
>From 3670d79c7051e6aa4b7a9d161f70580a398ea781 Mon Sep 17 00:00:00 2001
From: Aleksei Nurmukhametov <anurmukh at amd.com>
Date: Wed, 29 Oct 2025 13:19:41 +0000
Subject: [PATCH 3/3] WIP: fix formatting
---
libcxx/include/complex | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/complex b/libcxx/include/complex
index 21518b0e5ddb6..c41a4b725fc4e 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -1074,7 +1074,7 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> sqrt(const complex<_Tp>& __x) {
// exp
-#if _LIBCPP_HAS_C99_COMPLEX
+# if _LIBCPP_HAS_C99_COMPLEX
_LIBCPP_HIDE_FROM_ABI inline _Complex float __cexp(_Complex float __v) { return __builtin_cexpf(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex double __cexp(_Complex double __v) { return __builtin_cexp(__v); }
_LIBCPP_HIDE_FROM_ABI inline _Complex long double __cexp(_Complex long double __v) { return __builtin_cexpl(__v); }
@@ -1083,7 +1083,7 @@ template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
return complex<_Tp>(__from_builtin_tag(), std::__cexp(__x.__builtin()));
}
-#else
+# else
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
_Tp __i = __x.imag();
@@ -1100,14 +1100,14 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) {
return complex<_Tp>(__x.real(), __i);
}
}
+ _Tp __e = std::exp(__x.real());
if (std::isinf(__e)) {
_Tp __e2 = std::exp(__x.real() * _Tp(0.5));
return complex<_Tp>(__e2 * std::cos(__i) * __e2, __e2 * std::sin(__i) * __e2);
}
- _Tp __e = std::exp(__x.real());
return complex<_Tp>(__e * std::cos(__i), __e * std::sin(__i));
}
-#endif
+# endif
// pow
More information about the libcxx-commits
mailing list