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 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 +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 d8ec3d95c10ed..c41a4b725fc4e 100644 --- a/libcxx/include/complex +++ b/libcxx/include/complex @@ -1074,6 +1074,16 @@ _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); } + +template +_LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) { + return complex<_Tp>(__from_builtin_tag(), std::__cexp(__x.__builtin())); +} +# else template _LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) { _Tp __i = __x.imag(); @@ -1091,8 +1101,13 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> exp(const complex<_Tp>& __x) { } } _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); + } return complex<_Tp>(__e * std::cos(__i), __e * std::sin(__i)); } +# endif // 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 +// +//===----------------------------------------------------------------------===// + +// + +// template +// complex +// exp(const complex& x); + +#include +#include +#include + +#include "test_macros.h" + +template +void test_overflow_case() { + typedef std::complex 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(); + return 0; +}