[libcxx-commits] [libcxx] [libc++] Use _Complex for multiplication and division of complex floating point types (PR #83575)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Wed May 29 18:16:52 PDT 2024
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/83575
>From 7c65593fa03d27d872c40791febeb37b68dc04ca Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 14 Sep 2023 20:37:34 +0200
Subject: [PATCH 1/2] [libc++] Use _Complex for multiplication and division of
complex floating point types
This significantly simplifies the implementation and improves the codegen. The only downside is that the accuracy can be marginally worse, but that is up to the compiler to decide with this change, which means is can be controlled by compiler flags.
Spies: ldionne, Mordante, libcxx-commits
Differential Revision: https://reviews.llvm.org/D155312
---
libcxx/include/cmath | 155 ------------
libcxx/include/complex | 229 +++++++-----------
.../divide_equal_complex.pass.cpp | 23 +-
.../complex_divide_complex.pass.cpp | 9 +-
.../complex_times_complex.pass.cpp | 2 +-
.../scalar_divide_complex.pass.cpp | 6 +-
.../test/support/floating_pointer_helpers.h | 21 ++
7 files changed, 129 insertions(+), 316 deletions(-)
create mode 100644 libcxx/test/support/floating_pointer_helpers.h
diff --git a/libcxx/include/cmath b/libcxx/include/cmath
index dd194bbb55896..a20759758a910 100644
--- a/libcxx/include/cmath
+++ b/libcxx/include/cmath
@@ -610,161 +610,6 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR bool __constexpr_isfinite(_A1 __lcpp_x)
return __builtin_isfinite(__lcpp_x);
}
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI float __constexpr_copysign(float __x, float __y) _NOEXCEPT {
- return __builtin_copysignf(__x, __y);
-}
-
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI double __constexpr_copysign(double __x, double __y) _NOEXCEPT {
- return __builtin_copysign(__x, __y);
-}
-
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI long double
-__constexpr_copysign(long double __x, long double __y) _NOEXCEPT {
- return __builtin_copysignl(__x, __y);
-}
-
-template <class _A1,
- class _A2,
- __enable_if_t<std::is_arithmetic<_A1>::value && std::is_arithmetic<_A2>::value, int> = 0>
-_LIBCPP_CONSTEXPR inline _LIBCPP_HIDE_FROM_ABI typename __promote<_A1, _A2>::type
-__constexpr_copysign(_A1 __x, _A2 __y) _NOEXCEPT {
- typedef typename std::__promote<_A1, _A2>::type __result_type;
- static_assert((!(std::_IsSame<_A1, __result_type>::value && std::_IsSame<_A2, __result_type>::value)), "");
- return __builtin_copysign((__result_type)__x, (__result_type)__y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR float __constexpr_fabs(float __x) _NOEXCEPT {
- return __builtin_fabsf(__x);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR double __constexpr_fabs(double __x) _NOEXCEPT {
- return __builtin_fabs(__x);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR long double __constexpr_fabs(long double __x) _NOEXCEPT {
- return __builtin_fabsl(__x);
-}
-
-template <class _Tp, __enable_if_t<is_integral<_Tp>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR double __constexpr_fabs(_Tp __x) _NOEXCEPT {
- return __builtin_fabs(static_cast<double>(__x));
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 float __constexpr_fmax(float __x, float __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmaxf)
- if (__libcpp_is_constant_evaluated()) {
- if (std::__constexpr_isnan(__x))
- return __y;
- if (std::__constexpr_isnan(__y))
- return __x;
- return __x < __y ? __y : __x;
- }
-#endif
- return __builtin_fmaxf(__x, __y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 double __constexpr_fmax(double __x, double __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmax)
- if (__libcpp_is_constant_evaluated()) {
- if (std::__constexpr_isnan(__x))
- return __y;
- if (std::__constexpr_isnan(__y))
- return __x;
- return __x < __y ? __y : __x;
- }
-#endif
- return __builtin_fmax(__x, __y);
-}
-
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 long double
-__constexpr_fmax(long double __x, long double __y) _NOEXCEPT {
-#if !__has_constexpr_builtin(__builtin_fmaxl)
- if (__libcpp_is_constant_evaluated()) {
- if (std::__constexpr_isnan(__x))
- return __y;
- if (std::__constexpr_isnan(__y))
- return __x;
- return __x < __y ? __y : __x;
- }
-#endif
- return __builtin_fmaxl(__x, __y);
-}
-
-template <class _Tp, class _Up, __enable_if_t<is_arithmetic<_Tp>::value && is_arithmetic<_Up>::value, int> = 0>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename __promote<_Tp, _Up>::type
-__constexpr_fmax(_Tp __x, _Up __y) _NOEXCEPT {
- using __result_type = typename __promote<_Tp, _Up>::type;
- return std::__constexpr_fmax(static_cast<__result_type>(__x), static_cast<__result_type>(__y));
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 _Tp __constexpr_logb(_Tp __x) {
-#if !__has_constexpr_builtin(__builtin_logb)
- if (__libcpp_is_constant_evaluated()) {
- if (__x == _Tp(0)) {
- // raise FE_DIVBYZERO
- return -numeric_limits<_Tp>::infinity();
- }
-
- if (std::__constexpr_isinf(__x))
- return numeric_limits<_Tp>::infinity();
-
- if (std::__constexpr_isnan(__x))
- return numeric_limits<_Tp>::quiet_NaN();
-
- __x = std::__constexpr_fabs(__x);
- unsigned long long __exp = 0;
- while (__x >= numeric_limits<_Tp>::radix) {
- __x /= numeric_limits<_Tp>::radix;
- __exp += 1;
- }
- return _Tp(__exp);
- }
-#endif // !__has_constexpr_builtin(__builtin_logb)
- return __builtin_logb(__x);
-}
-
-template <class _Tp>
-_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _Tp __constexpr_scalbn(_Tp __x, int __exp) {
-#if !__has_constexpr_builtin(__builtin_scalbln)
- if (__libcpp_is_constant_evaluated()) {
- if (__x == _Tp(0))
- return __x;
-
- if (std::__constexpr_isinf(__x))
- return __x;
-
- if (__exp == _Tp(0))
- return __x;
-
- if (std::__constexpr_isnan(__x))
- return numeric_limits<_Tp>::quiet_NaN();
-
- _Tp __mult(1);
- if (__exp > 0) {
- __mult = numeric_limits<_Tp>::radix;
- --__exp;
- } else {
- ++__exp;
- __exp = -__exp;
- __mult /= numeric_limits<_Tp>::radix;
- }
-
- while (__exp > 0) {
- if (!(__exp & 1)) {
- __mult *= __mult;
- __exp >>= 1;
- } else {
- __x *= __mult;
- --__exp;
- }
- }
- return __x;
- }
-#endif // !__has_constexpr_builtin(__builtin_scalbln)
- return __builtin_scalbn(__x, __exp);
-}
-
#if _LIBCPP_STD_VER >= 20
template <typename _Fp>
_LIBCPP_HIDE_FROM_ABI constexpr _Fp __lerp(_Fp __a, _Fp __b, _Fp __t) noexcept {
diff --git a/libcxx/include/complex b/libcxx/include/complex
index a81f968143c4c..69a9fcce6f7ef 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -281,10 +281,19 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
class _LIBCPP_TEMPLATE_VIS complex;
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
operator*(const complex<_Tp>& __z, const complex<_Tp>& __w);
-template <class _Tp>
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator*(const complex<_Tp>& __z, const complex<_Tp>& __w);
+
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator/(const complex<_Tp>& __x, const complex<_Tp>& __y);
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
operator/(const complex<_Tp>& __x, const complex<_Tp>& __y);
@@ -384,6 +393,23 @@ class _LIBCPP_TEMPLATE_VIS complex<double>;
template <>
class _LIBCPP_TEMPLATE_VIS complex<long double>;
+struct __from_builtin_tag {};
+
+template <class _Tp>
+using __complex_t =
+ __conditional_t<is_same<_Tp, float>::value,
+ _Complex float,
+ __conditional_t<is_same<_Tp, double>::value, _Complex double, _Complex long double> >;
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR __complex_t<_Tp> __make_complex(_Tp __re, _Tp __im) {
+#if __has_builtin(__builtin_complex)
+ return __builtin_complex(__re, __im);
+#else
+ return __complex_t<_Tp>{__re, __im};
+#endif
+}
+
template <>
class _LIBCPP_TEMPLATE_VIS complex<float> {
float __re_;
@@ -393,6 +419,10 @@ public:
typedef float value_type;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(float __re = 0.0f, float __im = 0.0f) : __re_(__re), __im_(__im) {}
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex float __v)
+ : __re_(__real__ __v), __im_(__imag__ __v) {}
+
_LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<double>& __c);
_LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<long double>& __c);
@@ -402,6 +432,12 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex float __builtin() const { return std::__make_complex(__re_, __im_); }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex float __f) {
+ __re_ = __real__ __f;
+ __im_ = __imag__ __f;
+ }
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(float __re) {
__re_ = __re;
__im_ = value_type();
@@ -479,6 +515,10 @@ public:
typedef double value_type;
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(double __re = 0.0, double __im = 0.0) : __re_(__re), __im_(__im) {}
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex double __v)
+ : __re_(__real__ __v), __im_(__imag__ __v) {}
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<float>& __c);
_LIBCPP_HIDE_FROM_ABI explicit _LIBCPP_CONSTEXPR complex(const complex<long double>& __c);
@@ -488,6 +528,15 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex double __builtin() const {
+ return std::__make_complex(__re_, __im_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex double __f) {
+ __re_ = __real__ __f;
+ __im_ = __imag__ __f;
+ }
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(double __re) {
__re_ = __re;
__im_ = value_type();
@@ -566,6 +615,10 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(long double __re = 0.0L, long double __im = 0.0L)
: __re_(__re), __im_(__im) {}
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(__from_builtin_tag, _Complex long double __v)
+ : __re_(__real__ __v), __im_(__imag__ __v) {}
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<float>& __c);
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR complex(const complex<double>& __c);
@@ -575,6 +628,15 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void real(value_type __re) { __re_ = __re; }
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void imag(value_type __im) { __im_ = __im; }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Complex long double __builtin() const {
+ return std::__make_complex(__re_, __im_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 void __builtin(_Complex long double __f) {
+ __re_ = __real__ __f;
+ __im_ = __imag__ __f;
+ }
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex& operator=(long double __re) {
__re_ = __re;
__im_ = value_type();
@@ -709,7 +771,13 @@ operator-(const _Tp& __x, const complex<_Tp>& __y) {
return __t;
}
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator*(const complex<_Tp>& __lhs, const complex<_Tp>& __rhs) {
+ return complex<_Tp>(__from_builtin_tag(), __lhs.__builtin() * __rhs.__builtin());
+}
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> >
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
operator*(const complex<_Tp>& __z, const complex<_Tp>& __w) {
_Tp __a = __z.real();
@@ -717,78 +785,7 @@ operator*(const complex<_Tp>& __z, const complex<_Tp>& __w) {
_Tp __c = __w.real();
_Tp __d = __w.imag();
- // Avoid floating point operations that are invalid during constant evaluation
- if (__libcpp_is_constant_evaluated()) {
- bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
- bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
- bool __z_inf = std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b);
- bool __w_inf = std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d);
- bool __z_nan =
- !__z_inf && ((std::__constexpr_isnan(__a) && std::__constexpr_isnan(__b)) ||
- (std::__constexpr_isnan(__a) && __b == _Tp(0)) || (__a == _Tp(0) && std::__constexpr_isnan(__b)));
- bool __w_nan =
- !__w_inf && ((std::__constexpr_isnan(__c) && std::__constexpr_isnan(__d)) ||
- (std::__constexpr_isnan(__c) && __d == _Tp(0)) || (__c == _Tp(0) && std::__constexpr_isnan(__d)));
- if (__z_nan || __w_nan) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- if (__z_inf || __w_inf) {
- if (__z_zero || __w_zero) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
- }
- bool __z_nonzero_nan = !__z_inf && !__z_nan && (std::__constexpr_isnan(__a) || std::__constexpr_isnan(__b));
- bool __w_nonzero_nan = !__w_inf && !__w_nan && (std::__constexpr_isnan(__c) || std::__constexpr_isnan(__d));
- if (__z_nonzero_nan || __w_nonzero_nan) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- }
-
- _Tp __ac = __a * __c;
- _Tp __bd = __b * __d;
- _Tp __ad = __a * __d;
- _Tp __bc = __b * __c;
- _Tp __x = __ac - __bd;
- _Tp __y = __ad + __bc;
- if (std::__constexpr_isnan(__x) && std::__constexpr_isnan(__y)) {
- bool __recalc = false;
- if (std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b)) {
- __a = std::__constexpr_copysign(std::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
- __b = std::__constexpr_copysign(std::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
- if (std::__constexpr_isnan(__c))
- __c = std::__constexpr_copysign(_Tp(0), __c);
- if (std::__constexpr_isnan(__d))
- __d = std::__constexpr_copysign(_Tp(0), __d);
- __recalc = true;
- }
- if (std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d)) {
- __c = std::__constexpr_copysign(std::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
- __d = std::__constexpr_copysign(std::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
- if (std::__constexpr_isnan(__a))
- __a = std::__constexpr_copysign(_Tp(0), __a);
- if (std::__constexpr_isnan(__b))
- __b = std::__constexpr_copysign(_Tp(0), __b);
- __recalc = true;
- }
- if (!__recalc && (std::__constexpr_isinf(__ac) || std::__constexpr_isinf(__bd) || std::__constexpr_isinf(__ad) ||
- std::__constexpr_isinf(__bc))) {
- if (std::__constexpr_isnan(__a))
- __a = std::__constexpr_copysign(_Tp(0), __a);
- if (std::__constexpr_isnan(__b))
- __b = std::__constexpr_copysign(_Tp(0), __b);
- if (std::__constexpr_isnan(__c))
- __c = std::__constexpr_copysign(_Tp(0), __c);
- if (std::__constexpr_isnan(__d))
- __d = std::__constexpr_copysign(_Tp(0), __d);
- __recalc = true;
- }
- if (__recalc) {
- __x = _Tp(INFINITY) * (__a * __c - __b * __d);
- __y = _Tp(INFINITY) * (__a * __d + __b * __c);
- }
- }
- return complex<_Tp>(__x, __y);
+ return complex<_Tp>((__a * __c) - (__b * __d), (__a * __d) + (__b * __c));
}
template <class _Tp>
@@ -807,80 +804,22 @@ operator*(const _Tp& __x, const complex<_Tp>& __y) {
return __t;
}
-template <class _Tp>
+template <class _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> >
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
+operator/(const complex<_Tp>& __lhs, const complex<_Tp>& __rhs) {
+ return complex<_Tp>(__from_builtin_tag(), __lhs.__builtin() / __rhs.__builtin());
+}
+
+template <class _Tp, __enable_if_t<!is_floating_point<_Tp>::value, int> >
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 complex<_Tp>
operator/(const complex<_Tp>& __z, const complex<_Tp>& __w) {
- int __ilogbw = 0;
- _Tp __a = __z.real();
- _Tp __b = __z.imag();
- _Tp __c = __w.real();
- _Tp __d = __w.imag();
- _Tp __logbw = std::__constexpr_logb(std::__constexpr_fmax(std::__constexpr_fabs(__c), std::__constexpr_fabs(__d)));
- if (std::__constexpr_isfinite(__logbw)) {
- __ilogbw = static_cast<int>(__logbw);
- __c = std::__constexpr_scalbn(__c, -__ilogbw);
- __d = std::__constexpr_scalbn(__d, -__ilogbw);
- }
-
- // Avoid floating point operations that are invalid during constant evaluation
- if (__libcpp_is_constant_evaluated()) {
- bool __z_zero = __a == _Tp(0) && __b == _Tp(0);
- bool __w_zero = __c == _Tp(0) && __d == _Tp(0);
- bool __z_inf = std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b);
- bool __w_inf = std::__constexpr_isinf(__c) || std::__constexpr_isinf(__d);
- bool __z_nan =
- !__z_inf && ((std::__constexpr_isnan(__a) && std::__constexpr_isnan(__b)) ||
- (std::__constexpr_isnan(__a) && __b == _Tp(0)) || (__a == _Tp(0) && std::__constexpr_isnan(__b)));
- bool __w_nan =
- !__w_inf && ((std::__constexpr_isnan(__c) && std::__constexpr_isnan(__d)) ||
- (std::__constexpr_isnan(__c) && __d == _Tp(0)) || (__c == _Tp(0) && std::__constexpr_isnan(__d)));
- if ((__z_nan || __w_nan) || (__z_inf && __w_inf)) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- bool __z_nonzero_nan = !__z_inf && !__z_nan && (std::__constexpr_isnan(__a) || std::__constexpr_isnan(__b));
- bool __w_nonzero_nan = !__w_inf && !__w_nan && (std::__constexpr_isnan(__c) || std::__constexpr_isnan(__d));
- if (__z_nonzero_nan || __w_nonzero_nan) {
- if (__w_zero) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
- }
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- if (__w_inf) {
- return complex<_Tp>(_Tp(0), _Tp(0));
- }
- if (__z_inf) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
- }
- if (__w_zero) {
- if (__z_zero) {
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::quiet_NaN()), _Tp(0));
- }
- return complex<_Tp>(_Tp(numeric_limits<_Tp>::infinity()), _Tp(numeric_limits<_Tp>::infinity()));
- }
- }
+ _Tp __a = __z.real();
+ _Tp __b = __z.imag();
+ _Tp __c = __w.real();
+ _Tp __d = __w.imag();
_Tp __denom = __c * __c + __d * __d;
- _Tp __x = std::__constexpr_scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
- _Tp __y = std::__constexpr_scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
- if (std::__constexpr_isnan(__x) && std::__constexpr_isnan(__y)) {
- if ((__denom == _Tp(0)) && (!std::__constexpr_isnan(__a) || !std::__constexpr_isnan(__b))) {
- __x = std::__constexpr_copysign(_Tp(INFINITY), __c) * __a;
- __y = std::__constexpr_copysign(_Tp(INFINITY), __c) * __b;
- } else if ((std::__constexpr_isinf(__a) || std::__constexpr_isinf(__b)) && std::__constexpr_isfinite(__c) &&
- std::__constexpr_isfinite(__d)) {
- __a = std::__constexpr_copysign(std::__constexpr_isinf(__a) ? _Tp(1) : _Tp(0), __a);
- __b = std::__constexpr_copysign(std::__constexpr_isinf(__b) ? _Tp(1) : _Tp(0), __b);
- __x = _Tp(INFINITY) * (__a * __c + __b * __d);
- __y = _Tp(INFINITY) * (__b * __c - __a * __d);
- } else if (std::__constexpr_isinf(__logbw) && __logbw > _Tp(0) && std::__constexpr_isfinite(__a) &&
- std::__constexpr_isfinite(__b)) {
- __c = std::__constexpr_copysign(std::__constexpr_isinf(__c) ? _Tp(1) : _Tp(0), __c);
- __d = std::__constexpr_copysign(std::__constexpr_isinf(__d) ? _Tp(1) : _Tp(0), __d);
- __x = _Tp(0) * (__a * __c + __b * __d);
- __y = _Tp(0) * (__b * __c - __a * __d);
- }
- }
- return complex<_Tp>(__x, __y);
+ return complex<_Tp>((__a * __c + __b * __d) / __denom, (__b * __c - __a * __d) / __denom);
}
template <class _Tp>
diff --git a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
index af2561e4cb9e9..61e0d01f2ae4d 100644
--- a/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.member.ops/divide_equal_complex.pass.cpp
@@ -10,9 +10,10 @@
// complex& operator/=(const complex& rhs); // constexpr in C++20
-#include <complex>
#include <cassert>
+#include <complex>
+#include "floating_pointer_helpers.h"
#include "test_macros.h"
template <class T>
@@ -22,28 +23,28 @@ test()
{
std::complex<T> c(-4, 7.5);
const std::complex<T> c2(1.5, 2.5);
- assert(c.real() == -4);
- assert(c.imag() == 7.5);
+ assert(is_close(c.real(), T(-4)));
+ assert(is_close(c.imag(), T(7.5)));
c /= c2;
- assert(c.real() == 1.5);
- assert(c.imag() == 2.5);
+ assert(is_close(c.real(), T(1.5)));
+ assert(is_close(c.imag(), T(2.5)));
c /= c2;
- assert(c.real() == 1);
- assert(c.imag() == 0);
+ assert(is_close(c.real(), T(1)));
+ assert(is_close(c.imag(), T(0)));
std::complex<T> c3;
c3 = c;
std::complex<int> ic (1,1);
c3 /= ic;
- assert(c3.real() == 0.5);
- assert(c3.imag() == -0.5);
+ assert(is_close(c3.real(), T(0.5)));
+ assert(is_close(c3.imag(), T(-0.5)));
c3 = c;
std::complex<float> fc (1,1);
c3 /= fc;
- assert(c3.real() == 0.5);
- assert(c3.imag() == -0.5);
+ assert(is_close(c3.real(), T(0.5)));
+ assert(is_close(c3.imag(), T(-0.5)));
return true;
}
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
index 7119710a95557..d12dfd994b0ae 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_divide_complex.pass.cpp
@@ -12,11 +12,12 @@
// complex<T>
// operator/(const complex<T>& lhs, const complex<T>& rhs); // constexpr in C++20
-// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=5000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
-#include <complex>
#include <cassert>
+#include <complex>
+#include "floating_pointer_helpers.h"
#include "test_macros.h"
#include "../cases.h"
@@ -27,7 +28,9 @@ test()
{
const std::complex<T> lhs(-4.0, 7.5);
const std::complex<T> rhs(1.5, 2.5);
- assert(lhs / rhs == std::complex<T>(1.5, 2.5));
+ const std::complex<T> c = lhs / rhs;
+ assert(is_close(c.real(), T(1.5)));
+ assert(is_close(c.imag(), T(2.5)));
return true;
}
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
index 38ec71e3a00a5..817d6cdf492d3 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/complex_times_complex.pass.cpp
@@ -12,7 +12,7 @@
// complex<T>
// operator*(const complex<T>& lhs, const complex<T>& rhs); // constexpr in C++20
-// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=5000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000
#include <complex>
#include <cassert>
diff --git a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
index 50981216cc5f0..f32b560c1e864 100644
--- a/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
+++ b/libcxx/test/std/numerics/complex.number/complex.ops/scalar_divide_complex.pass.cpp
@@ -24,7 +24,11 @@ test()
{
const T lhs(-8.5);
const std::complex<T> rhs(1.5, 2.5);
- assert(lhs / rhs == std::complex<T>(-1.5, 2.5));
+ const std::complex<T> c = lhs / rhs;
+ assert(c.real() >= T(-1.500000000000001));
+ assert(c.real() <= T(-1.499999999999999));
+ assert(c.imag() >= T(2.499999999999999));
+ assert(c.imag() <= T(2.500000000000001));
return true;
}
diff --git a/libcxx/test/support/floating_pointer_helpers.h b/libcxx/test/support/floating_pointer_helpers.h
new file mode 100644
index 0000000000000..5943425bab018
--- /dev/null
+++ b/libcxx/test/support/floating_pointer_helpers.h
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_SUPPORT_FLOATING_POINT_HELPERS_H
+#define TEST_SUPPORT_FLOATING_POINT_HELPERS_H
+
+#include <limits>
+
+#include "test_macros.h"
+
+template <class T>
+TEST_CONSTEXPR_CXX20 bool is_close(T v, T comp) {
+ return v <= comp + std::numeric_limits<T>::epsilon() && v >= comp - std::numeric_limits<T>::epsilon();
+}
+
+#endif // TEST_SUPPORT_FLOATING_POINT_HELPERS_H
>From e55628a56e4f674ba8c0a7072d861b5298da7abd Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Wed, 29 May 2024 18:15:59 -0700
Subject: [PATCH 2/2] Implement missing compiler-rt functions in libc++
---
libcxx/src/CMakeLists.txt | 1 +
.../src/support/win32/compiler_rt_shims.cpp | 168 ++++++++++++++++++
2 files changed, 169 insertions(+)
create mode 100644 libcxx/src/support/win32/compiler_rt_shims.cpp
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 65e6ce2c4da43..ed90a878c2a5e 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -99,6 +99,7 @@ endif()
if(WIN32)
list(APPEND LIBCXX_SOURCES
+ support/win32/compiler_rt_shims.cpp
support/win32/locale_win32.cpp
support/win32/support.cpp
)
diff --git a/libcxx/src/support/win32/compiler_rt_shims.cpp b/libcxx/src/support/win32/compiler_rt_shims.cpp
new file mode 100644
index 0000000000000..451705422586a
--- /dev/null
+++ b/libcxx/src/support/win32/compiler_rt_shims.cpp
@@ -0,0 +1,168 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+//
+// This file reimplements builtins that are normally provided by compiler-rt, which is
+// not provided on Windows. This should go away once compiler-rt is shipped on Windows.
+//
+
+#include <cmath>
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex double __muldc3(double __a, double __b, double __c, double __d) {
+ double __ac = __a * __c;
+ double __bd = __b * __d;
+ double __ad = __a * __d;
+ double __bc = __b * __c;
+ _Complex double z;
+ if (std::isnan(__real__ z) && std::isnan(__imag__ z)) {
+ int __recalc = 0;
+ if (std::isinf(__a) || std::isinf(__b)) {
+ __a = std::copysign(std::isinf(__a) ? 1 : 0, __a);
+ __b = std::copysign(std::isinf(__b) ? 1 : 0, __b);
+ if (std::isnan(__c))
+ __c = std::copysign(0, __c);
+ if (std::isnan(__d))
+ __d = std::copysign(0, __d);
+ __recalc = 1;
+ }
+ if (std::isinf(__c) || std::isinf(__d)) {
+ __c = std::copysign(std::isinf(__c) ? 1 : 0, __c);
+ __d = std::copysign(std::isinf(__d) ? 1 : 0, __d);
+ if (std::isnan(__a))
+ __a = std::copysign(0, __a);
+ if (std::isnan(__b))
+ __b = std::copysign(0, __b);
+ __recalc = 1;
+ }
+ if (!__recalc && (std::isinf(__ac) || std::isinf(__bd) || std::isinf(__ad) || std::isinf(__bc))) {
+ if (std::isnan(__a))
+ __a = std::copysign(0, __a);
+ if (std::isnan(__b))
+ __b = std::copysign(0, __b);
+ if (std::isnan(__c))
+ __c = std::copysign(0, __c);
+ if (std::isnan(__d))
+ __d = std::copysign(0, __d);
+ __recalc = 1;
+ }
+ if (__recalc) {
+ __real__ z = HUGE_VAL * (__a * __c - __b * __d);
+ __imag__ z = HUGE_VAL * (__a * __d + __b * __c);
+ }
+ }
+ return z;
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex float __mulsc3(float __a, float __b, float __c, float __d) {
+ float __ac = __a * __c;
+ float __bd = __b * __d;
+ float __ad = __a * __d;
+ float __bc = __b * __c;
+ _Complex float z;
+ __real__ z = __ac - __bd;
+ __imag__ z = __ad + __bc;
+ if (std::isnan(__real__ z) && std::isnan(__imag__ z)) {
+ int __recalc = 0;
+ if (std::isinf(__a) || std::isinf(__b)) {
+ __a = std::copysignf(std::isinf(__a) ? 1 : 0, __a);
+ __b = std::copysignf(std::isinf(__b) ? 1 : 0, __b);
+ if (std::isnan(__c))
+ __c = std::copysignf(0, __c);
+ if (std::isnan(__d))
+ __d = std::copysignf(0, __d);
+ __recalc = 1;
+ }
+ if (std::isinf(__c) || std::isinf(__d)) {
+ __c = std::copysignf(std::isinf(__c) ? 1 : 0, __c);
+ __d = std::copysignf(std::isinf(__d) ? 1 : 0, __d);
+ if (std::isnan(__a))
+ __a = std::copysignf(0, __a);
+ if (std::isnan(__b))
+ __b = std::copysignf(0, __b);
+ __recalc = 1;
+ }
+ if (!__recalc && (std::isinf(__ac) || std::isinf(__bd) || std::isinf(__ad) || std::isinf(__bc))) {
+ if (std::isnan(__a))
+ __a = std::copysignf(0, __a);
+ if (std::isnan(__b))
+ __b = std::copysignf(0, __b);
+ if (std::isnan(__c))
+ __c = std::copysignf(0, __c);
+ if (std::isnan(__d))
+ __d = std::copysignf(0, __d);
+ __recalc = 1;
+ }
+ if (__recalc) {
+ __real__ z = HUGE_VALF * (__a * __c - __b * __d);
+ __imag__ z = HUGE_VALF * (__a * __d + __b * __c);
+ }
+ }
+ return z;
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex double __divdc3(double __a, double __b, double __c, double __d) {
+ int __ilogbw = 0;
+ double __logbw = std::logb(std::fmax(std::fabs(__c), std::fabs(__d)));
+ if (std::isfinite(__logbw)) {
+ __ilogbw = (int)__logbw;
+ __c = std::scalbn(__c, -__ilogbw);
+ __d = std::scalbn(__d, -__ilogbw);
+ }
+ double __denom = __c * __c + __d * __d;
+ _Complex double z;
+ __real__ z = std::scalbn((__a * __c + __b * __d) / __denom, -__ilogbw);
+ __imag__ z = std::scalbn((__b * __c - __a * __d) / __denom, -__ilogbw);
+ if (std::isnan(__real__ z) && std::isnan(__imag__ z)) {
+ if ((__denom == 0.0) && (!std::isnan(__a) || !std::isnan(__b))) {
+ __real__ z = std::copysign(HUGE_VAL, __c) * __a;
+ __imag__ z = std::copysign(HUGE_VAL, __c) * __b;
+ } else if ((std::isinf(__a) || std::isinf(__b)) && std::isfinite(__c) && std::isfinite(__d)) {
+ __a = std::copysign(std::isinf(__a) ? 1.0 : 0.0, __a);
+ __b = std::copysign(std::isinf(__b) ? 1.0 : 0.0, __b);
+ __real__ z = HUGE_VAL * (__a * __c + __b * __d);
+ __imag__ z = HUGE_VAL * (__b * __c - __a * __d);
+ } else if (std::isinf(__logbw) && __logbw > 0.0 && std::isfinite(__a) && std::isfinite(__b)) {
+ __c = std::copysign(std::isinf(__c) ? 1.0 : 0.0, __c);
+ __d = std::copysign(std::isinf(__d) ? 1.0 : 0.0, __d);
+ __real__ z = 0.0 * (__a * __c + __b * __d);
+ __imag__ z = 0.0 * (__b * __c - __a * __d);
+ }
+ }
+ return z;
+}
+
+extern "C" _LIBCPP_EXPORTED_FROM_ABI _Complex float __divsc3(float __a, float __b, float __c, float __d) {
+ int __ilogbw = 0;
+ float __logbw = std::logbf(__builtin_fmaxf(std::fabsf(__c), std::fabsf(__d)));
+ if (std::isfinite(__logbw)) {
+ __ilogbw = (int)__logbw;
+ __c = std::scalbnf(__c, -__ilogbw);
+ __d = std::scalbnf(__d, -__ilogbw);
+ }
+ float __denom = __c * __c + __d * __d;
+ _Complex float z;
+ __real__ z = std::scalbnf((__a * __c + __b * __d) / __denom, -__ilogbw);
+ __imag__ z = std::scalbnf((__b * __c - __a * __d) / __denom, -__ilogbw);
+ if (std::isnan(__real__ z) && std::isnan(__imag__ z)) {
+ if ((__denom == 0) && (!std::isnan(__a) || !std::isnan(__b))) {
+ __real__ z = std::copysignf(HUGE_VALF, __c) * __a;
+ __imag__ z = std::copysignf(HUGE_VALF, __c) * __b;
+ } else if ((std::isinf(__a) || std::isinf(__b)) && std::isfinite(__c) && std::isfinite(__d)) {
+ __a = std::copysignf(std::isinf(__a) ? 1 : 0, __a);
+ __b = std::copysignf(std::isinf(__b) ? 1 : 0, __b);
+ __real__ z = HUGE_VALF * (__a * __c + __b * __d);
+ __imag__ z = HUGE_VALF * (__b * __c - __a * __d);
+ } else if (std::isinf(__logbw) && __logbw > 0 && std::isfinite(__a) && std::isfinite(__b)) {
+ __c = std::copysignf(std::isinf(__c) ? 1 : 0, __c);
+ __d = std::copysignf(std::isinf(__d) ? 1 : 0, __d);
+ __real__ z = 0 * (__a * __c + __b * __d);
+ __imag__ z = 0 * (__b * __c - __a * __d);
+ }
+ }
+ return z;
+}
More information about the libcxx-commits
mailing list