-
Notifications
You must be signed in to change notification settings - Fork 12.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libcxx] makes tanh(complex<float>)
work for large values
#122194
base: main
Are you sure you want to change the base?
Conversation
By attempting to compute `sin(2x)` and `cos(2x)`, we were inadvertently producing `complex<float>(NaN, NaN)` for any `|x| > 2^127`, since `2x` would be computed as `inf`, and `sin(inf) == cos(inf) == NaN`. This commit relies on trig identities to sidestep this issue and always produce a valid answer for large values of `tanh`. The test cases only handled `double`, so they have been expanded to test `float` and `long double` as well.
@llvm/pr-subscribers-libcxx Author: Christopher Di Bella (cjdb) ChangesBy attempting to compute This commit relies on trig identities to sidestep this issue and always produce a valid answer for large values of Patch is 117.15 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/122194.diff 28 Files Affected:
diff --git a/libcxx/include/complex b/libcxx/include/complex
index df18159595b34d..5f425206100ce2 100644
--- a/libcxx/include/complex
+++ b/libcxx/include/complex
@@ -1236,6 +1236,36 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> cosh(const complex<_Tp>& __x) {
// tanh
+template<class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __sin2(const _Tp __x) noexcept
+{
+ static_assert(std::is_arithmetic<_Tp>::value, "requires an arithmetic type");
+ return 2 * std::sin(__x) * std::cos(__x);
+}
+
+template<class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __sinh2(const _Tp __x) noexcept
+{
+ static_assert(std::is_arithmetic<_Tp>::value, "requires an arithmetic type");
+ return 2 * std::sinh(__x) * std::cosh(__x);
+}
+
+template<class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __cos2(const _Tp __x) noexcept
+{
+ static_assert(std::is_arithmetic<_Tp>::value, "requires an arithmetic type");
+ const _Tp __cos = std::cos(__x);
+ return 2 * __cos * __cos - 1;
+}
+
+template<class _Tp>
+_LIBCPP_HIDE_FROM_ABI _Tp __cosh2(const _Tp __x) noexcept
+{
+ static_assert(std::is_arithmetic<_Tp>::value, "requires an arithmetic type");
+ const _Tp __cosh = std::cosh(__x);
+ return 2 * __cosh * __cosh - 1;
+}
+
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI complex<_Tp> tanh(const complex<_Tp>& __x) {
if (std::isinf(__x.real())) {
@@ -1245,13 +1275,11 @@ _LIBCPP_HIDE_FROM_ABI complex<_Tp> tanh(const complex<_Tp>& __x) {
}
if (std::isnan(__x.real()) && __x.imag() == 0)
return __x;
- _Tp __2r(_Tp(2) * __x.real());
- _Tp __2i(_Tp(2) * __x.imag());
- _Tp __d(std::cosh(__2r) + std::cos(__2i));
- _Tp __2rsh(std::sinh(__2r));
+ _Tp __d(std::__cosh2(__x.real()) + std::__cos2(__x.imag()));
+ _Tp __2rsh(std::__sinh2(__x.real()));
if (std::isinf(__2rsh) && std::isinf(__d))
- return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __2i > _Tp(0) ? _Tp(0) : _Tp(-0.));
- return complex<_Tp>(__2rsh / __d, std::sin(__2i) / __d);
+ return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __x.imag() > _Tp(0) ? _Tp(0) : _Tp(-0.));
+ return complex<_Tp>(__2rsh / __d, std::__sin2(__x.imag()) / __d);
}
// asin
diff --git a/libcxx/test/std/numerics/complex.number/cases.h b/libcxx/test/std/numerics/complex.number/cases.h
index b360e1423ff576..37d327450311ae 100644
--- a/libcxx/test/std/numerics/complex.number/cases.h
+++ b/libcxx/test/std/numerics/complex.number/cases.h
@@ -15,184 +15,201 @@
#include <cassert>
#include <complex>
+#include <limits>
#include <type_traits>
#include "test_macros.h"
-TEST_CONSTEXPR_CXX20 const std::complex<double> testcases[] =
+template<class T>
+TEST_CONSTEXPR_CXX20 const std::complex<T> testcases[] =
{
- std::complex<double>( 1.e-6, 1.e-6),
- std::complex<double>(-1.e-6, 1.e-6),
- std::complex<double>(-1.e-6, -1.e-6),
- std::complex<double>( 1.e-6, -1.e-6),
-
- std::complex<double>( 1.e+6, 1.e-6),
- std::complex<double>(-1.e+6, 1.e-6),
- std::complex<double>(-1.e+6, -1.e-6),
- std::complex<double>( 1.e+6, -1.e-6),
-
- std::complex<double>( 1.e-6, 1.e+6),
- std::complex<double>(-1.e-6, 1.e+6),
- std::complex<double>(-1.e-6, -1.e+6),
- std::complex<double>( 1.e-6, -1.e+6),
-
- std::complex<double>( 1.e+6, 1.e+6),
- std::complex<double>(-1.e+6, 1.e+6),
- std::complex<double>(-1.e+6, -1.e+6),
- std::complex<double>( 1.e+6, -1.e+6),
-
- std::complex<double>(-0, -1.e-6),
- std::complex<double>(-0, 1.e-6),
- std::complex<double>(-0, 1.e+6),
- std::complex<double>(-0, -1.e+6),
- std::complex<double>( 0, -1.e-6),
- std::complex<double>( 0, 1.e-6),
- std::complex<double>( 0, 1.e+6),
- std::complex<double>( 0, -1.e+6),
-
- std::complex<double>(-1.e-6, -0),
- std::complex<double>( 1.e-6, -0),
- std::complex<double>( 1.e+6, -0),
- std::complex<double>(-1.e+6, -0),
- std::complex<double>(-1.e-6, 0),
- std::complex<double>( 1.e-6, 0),
- std::complex<double>( 1.e+6, 0),
- std::complex<double>(-1.e+6, 0),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(-2, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(-1, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(-0.5, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(-0., std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(+0., std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(0.5, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(1, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(2, std::numeric_limits<double>::quiet_NaN()),
- std::complex<double>(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::quiet_NaN()),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), -std::numeric_limits<double>::infinity()),
- std::complex<double>(-std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()),
- std::complex<double>(-2, -std::numeric_limits<double>::infinity()),
- std::complex<double>(-1, -std::numeric_limits<double>::infinity()),
- std::complex<double>(-0.5, -std::numeric_limits<double>::infinity()),
- std::complex<double>(-0., -std::numeric_limits<double>::infinity()),
- std::complex<double>(+0., -std::numeric_limits<double>::infinity()),
- std::complex<double>(0.5, -std::numeric_limits<double>::infinity()),
- std::complex<double>(1, -std::numeric_limits<double>::infinity()),
- std::complex<double>(2, -std::numeric_limits<double>::infinity()),
- std::complex<double>(std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity()),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), -2),
- std::complex<double>(-std::numeric_limits<double>::infinity(), -2),
- std::complex<double>(-2, -2),
- std::complex<double>(-1, -2),
- std::complex<double>(-0.5, -2),
- std::complex<double>(-0., -2),
- std::complex<double>(+0., -2),
- std::complex<double>(0.5, -2),
- std::complex<double>(1, -2),
- std::complex<double>(2, -2),
- std::complex<double>(std::numeric_limits<double>::infinity(), -2),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), -1),
- std::complex<double>(-std::numeric_limits<double>::infinity(), -1),
- std::complex<double>(-2, -1),
- std::complex<double>(-1, -1),
- std::complex<double>(-0.5, -1),
- std::complex<double>(-0., -1),
- std::complex<double>(+0., -1),
- std::complex<double>(0.5, -1),
- std::complex<double>(1, -1),
- std::complex<double>(2, -1),
- std::complex<double>(std::numeric_limits<double>::infinity(), -1),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), -0.5),
- std::complex<double>(-std::numeric_limits<double>::infinity(), -0.5),
- std::complex<double>(-2, -0.5),
- std::complex<double>(-1, -0.5),
- std::complex<double>(-0.5, -0.5),
- std::complex<double>(-0., -0.5),
- std::complex<double>(+0., -0.5),
- std::complex<double>(0.5, -0.5),
- std::complex<double>(1, -0.5),
- std::complex<double>(2, -0.5),
- std::complex<double>(std::numeric_limits<double>::infinity(), -0.5),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), -0.),
- std::complex<double>(-std::numeric_limits<double>::infinity(), -0.),
- std::complex<double>(-2, -0.),
- std::complex<double>(-1, -0.),
- std::complex<double>(-0.5, -0.),
- std::complex<double>(-0., -0.),
- std::complex<double>(+0., -0.),
- std::complex<double>(0.5, -0.),
- std::complex<double>(1, -0.),
- std::complex<double>(2, -0.),
- std::complex<double>(std::numeric_limits<double>::infinity(), -0.),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), +0.),
- std::complex<double>(-std::numeric_limits<double>::infinity(), +0.),
- std::complex<double>(-2, +0.),
- std::complex<double>(-1, +0.),
- std::complex<double>(-0.5, +0.),
- std::complex<double>(-0., +0.),
- std::complex<double>(+0., +0.),
- std::complex<double>(0.5, +0.),
- std::complex<double>(1, +0.),
- std::complex<double>(2, +0.),
- std::complex<double>(std::numeric_limits<double>::infinity(), +0.),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.5),
- std::complex<double>(-std::numeric_limits<double>::infinity(), 0.5),
- std::complex<double>(-2, 0.5),
- std::complex<double>(-1, 0.5),
- std::complex<double>(-0.5, 0.5),
- std::complex<double>(-0., 0.5),
- std::complex<double>(+0., 0.5),
- std::complex<double>(0.5, 0.5),
- std::complex<double>(1, 0.5),
- std::complex<double>(2, 0.5),
- std::complex<double>(std::numeric_limits<double>::infinity(), 0.5),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 1),
- std::complex<double>(-std::numeric_limits<double>::infinity(), 1),
- std::complex<double>(-2, 1),
- std::complex<double>(-1, 1),
- std::complex<double>(-0.5, 1),
- std::complex<double>(-0., 1),
- std::complex<double>(+0., 1),
- std::complex<double>(0.5, 1),
- std::complex<double>(1, 1),
- std::complex<double>(2, 1),
- std::complex<double>(std::numeric_limits<double>::infinity(), 1),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 2),
- std::complex<double>(-std::numeric_limits<double>::infinity(), 2),
- std::complex<double>(-2, 2),
- std::complex<double>(-1, 2),
- std::complex<double>(-0.5, 2),
- std::complex<double>(-0., 2),
- std::complex<double>(+0., 2),
- std::complex<double>(0.5, 2),
- std::complex<double>(1, 2),
- std::complex<double>(2, 2),
- std::complex<double>(std::numeric_limits<double>::infinity(), 2),
-
- std::complex<double>(std::numeric_limits<double>::quiet_NaN(), std::numeric_limits<double>::infinity()),
- std::complex<double>(-std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity()),
- std::complex<double>(-2, std::numeric_limits<double>::infinity()),
- std::complex<double>(-1, std::numeric_limits<double>::infinity()),
- std::complex<double>(-0.5, std::numeric_limits<double>::infinity()),
- std::complex<double>(-0., std::numeric_limits<double>::infinity()),
- std::complex<double>(+0., std::numeric_limits<double>::infinity()),
- std::complex<double>(0.5, std::numeric_limits<double>::infinity()),
- std::complex<double>(1, std::numeric_limits<double>::infinity()),
- std::complex<double>(2, std::numeric_limits<double>::infinity()),
- std::complex<double>(std::numeric_limits<double>::infinity(), std::numeric_limits<double>::infinity())
+ std::complex<T>( 1.e-6, 1.e-6),
+ std::complex<T>(-1.e-6, 1.e-6),
+ std::complex<T>(-1.e-6, -1.e-6),
+ std::complex<T>( 1.e-6, -1.e-6),
+
+ std::complex<T>( 1.e+6, 1.e-6),
+ std::complex<T>(-1.e+6, 1.e-6),
+ std::complex<T>(-1.e+6, -1.e-6),
+ std::complex<T>( 1.e+6, -1.e-6),
+
+ std::complex<T>( 1.e-6, 1.e+6),
+ std::complex<T>(-1.e-6, 1.e+6),
+ std::complex<T>(-1.e-6, -1.e+6),
+ std::complex<T>( 1.e-6, -1.e+6),
+
+ std::complex<T>( 1.e+6, 1.e+6),
+ std::complex<T>(-1.e+6, 1.e+6),
+ std::complex<T>(-1.e+6, -1.e+6),
+ std::complex<T>( 1.e+6, -1.e+6),
+
+ std::complex<T>(-0, -1.e-6),
+ std::complex<T>(-0, 1.e-6),
+ std::complex<T>(-0, 1.e+6),
+ std::complex<T>(-0, -1.e+6),
+ std::complex<T>( 0, -1.e-6),
+ std::complex<T>( 0, 1.e-6),
+ std::complex<T>( 0, 1.e+6),
+ std::complex<T>( 0, -1.e+6),
+
+ std::complex<T>(-1.e-6, -0),
+ std::complex<T>( 1.e-6, -0),
+ std::complex<T>( 1.e+6, -0),
+ std::complex<T>(-1.e+6, -0),
+ std::complex<T>(-1.e-6, 0),
+ std::complex<T>( 1.e-6, 0),
+ std::complex<T>( 1.e+6, 0),
+ std::complex<T>(-1.e+6, 0),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(-2, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(-1, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(-0.5, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(-0., std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(+0., std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(0.5, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(1, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(2, std::numeric_limits<T>::quiet_NaN()),
+ std::complex<T>(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::quiet_NaN()),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), -std::numeric_limits<T>::infinity()),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity()),
+ std::complex<T>(-2, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(-1, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(-0.5, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(-0., -std::numeric_limits<T>::infinity()),
+ std::complex<T>(+0., -std::numeric_limits<T>::infinity()),
+ std::complex<T>(0.5, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(1, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(2, -std::numeric_limits<T>::infinity()),
+ std::complex<T>(std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity()),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), -2),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), -2),
+ std::complex<T>(-2, -2),
+ std::complex<T>(-1, -2),
+ std::complex<T>(-0.5, -2),
+ std::complex<T>(-0., -2),
+ std::complex<T>(+0., -2),
+ std::complex<T>(0.5, -2),
+ std::complex<T>(1, -2),
+ std::complex<T>(2, -2),
+ std::complex<T>(std::numeric_limits<T>::infinity(), -2),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), -1),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), -1),
+ std::complex<T>(-2, -1),
+ std::complex<T>(-1, -1),
+ std::complex<T>(-0.5, -1),
+ std::complex<T>(-0., -1),
+ std::complex<T>(+0., -1),
+ std::complex<T>(0.5, -1),
+ std::complex<T>(1, -1),
+ std::complex<T>(2, -1),
+ std::complex<T>(std::numeric_limits<T>::infinity(), -1),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), -0.5),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), -0.5),
+ std::complex<T>(-2, -0.5),
+ std::complex<T>(-1, -0.5),
+ std::complex<T>(-0.5, -0.5),
+ std::complex<T>(-0., -0.5),
+ std::complex<T>(+0., -0.5),
+ std::complex<T>(0.5, -0.5),
+ std::complex<T>(1, -0.5),
+ std::complex<T>(2, -0.5),
+ std::complex<T>(std::numeric_limits<T>::infinity(), -0.5),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), -0.),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), -0.),
+ std::complex<T>(-2, -0.),
+ std::complex<T>(-1, -0.),
+ std::complex<T>(-0.5, -0.),
+ std::complex<T>(-0., -0.),
+ std::complex<T>(+0., -0.),
+ std::complex<T>(0.5, -0.),
+ std::complex<T>(1, -0.),
+ std::complex<T>(2, -0.),
+ std::complex<T>(std::numeric_limits<T>::infinity(), -0.),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), +0.),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), +0.),
+ std::complex<T>(-2, +0.),
+ std::complex<T>(-1, +0.),
+ std::complex<T>(-0.5, +0.),
+ std::complex<T>(-0., +0.),
+ std::complex<T>(+0., +0.),
+ std::complex<T>(0.5, +0.),
+ std::complex<T>(1, +0.),
+ std::complex<T>(2, +0.),
+ std::complex<T>(std::numeric_limits<T>::infinity(), +0.),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), 0.5),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), 0.5),
+ std::complex<T>(-2, 0.5),
+ std::complex<T>(-1, 0.5),
+ std::complex<T>(-0.5, 0.5),
+ std::complex<T>(-0., 0.5),
+ std::complex<T>(+0., 0.5),
+ std::complex<T>(0.5, 0.5),
+ std::complex<T>(1, 0.5),
+ std::complex<T>(2, 0.5),
+ std::complex<T>(std::numeric_limits<T>::infinity(), 0.5),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), 1),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), 1),
+ std::complex<T>(-2, 1),
+ std::complex<T>(-1, 1),
+ std::complex<T>(-0.5, 1),
+ std::complex<T>(-0., 1),
+ std::complex<T>(+0., 1),
+ std::complex<T>(0.5, 1),
+ std::complex<T>(1, 1),
+ std::complex<T>(2, 1),
+ std::complex<T>(std::numeric_limits<T>::infinity(), 1),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), 2),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), 2),
+ std::complex<T>(-2, 2),
+ std::complex<T>(-1, 2),
+ std::complex<T>(-0.5, 2),
+ std::complex<T>(-0., 2),
+ std::complex<T>(+0., 2),
+ std::complex<T>(0.5, 2),
+ std::complex<T>(1, 2),
+ std::complex<T>(2, 2),
+ std::complex<T>(std::numeric_limits<T>::infinity(), 2),
+
+ std::complex<T>(std::numeric_limits<T>::quiet_NaN(), std::numeric_limits<T>::infinity()),
+ std::complex<T>(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()),
+ std::complex<T>(-2, std::numeric_limits<T>::infinity()),
+ std::complex<T>(-1, std::numeric_limits<T>::infinity()),
+ std::complex<T>(-0.5, std::numeric_limits<T>::infinity()),
+ std::complex<T>(-0., std::numeric_limits<T>::infinity()),
+ std::complex<T>(+0., std::numeric_limits<T>::infinity()),
+ std::complex<T>(0.5, std::numeric_limits<T>::infinity()),
+ std::complex<T>(1, std::numeric_limits<T>::infinity()),
+ std::complex<T>(2, std::numeric_limits<T>::infinity()),
+ std::complex<T>(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity()),
+
+ std::complex<T>(std::numeric_limits<T>::max(), 1),
+ std::complex<T>(std::numeric_limits<T>::max(), -1),
+ std::complex<T>(std::numeric_limits<T>::lowest(), 1),
+ std::complex<T>(std::numeric_limits<T>::lowest(), -1),
+
+ std::complex<T>(1, std::numeric_limits<T>::max()),
+ std::complex<T>(1, std::numeric_limits<T>::lowest()),
+ std::complex<T>(-1, std::numeric_limits<T>::max()),
+ std::complex<T>(-1, std::numeric_limits<T>::lowest()),
+
+ std::complex<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::max()),
+ std::complex<T>(std::numeric_limits<T>::max(), std::numeric_limits<T>::lowest()),
+ std::complex<T>(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::max()),
+ std::complex<T>(std::numeric_limits<T>::lowest(), std::numeric_limits<T>::lowest()),
};
-enum {zero, non_zero, inf, NaN, non_zero_nan};
+enum {zero, non_zero, lowest_value, maximum_value, inf, NaN, non_zero_nan};
template <class T, typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0>
TEST_CONSTEXPR_CXX20 bool test_isinf(T v) {
@@ -227,12 +244,17 @@ classify(const std::complex<T>& x)
return NaN;
return non_zero_nan;
}
+ if (x.real() == std::numeric_limits<T>::max() || x.imag() == std::numeric_limits<T>::max())
+ return maximum_value;
+ if (x.real() == std::numeric_limits<T>::lowest() || x.imag() == std::numeric_limits<T>::lowest())
+ return lowest_value;
return non_zero;
}
+template<class T>
inline
int
-classify(double x)
+classify(T x)
{
if (x == 0)
return zero;
@@ -240,6 +262,10 @@ classify(double x)
return inf;
if (std::isnan(x))
return NaN;
+ if (x == std::numeric_limits<T>::max())
+ return maximum_value;
+ if (x == std::numeric_limits<T>::lowest())
+ return lowest_value;
return non_zero;
}
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 d12dfd994b0ae9..bc0bdd0ec261dd 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,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=2000000
+// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2131685
#include <cassert>
#include <complex>
@@ -34,24 +34,30 @@ test()
return true;
}
+...
[truncated]
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
if (std::isinf(__2rsh) && std::isinf(__d)) | ||
return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __2i > _Tp(0) ? _Tp(0) : _Tp(-0.)); | ||
return complex<_Tp>(__2rsh / __d, std::sin(__2i) / __d); | ||
return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), __x.imag() > _Tp(0) ? _Tp(0) : _Tp(-0.)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of switching to a different formula, which is actually less accurate for the normal range in the current form, I think it is better to do the cutoff a lot earlier. This return is actually correct (for the default rounding mode) when:
so for the real part, to prevent overflow, some simple check like:
std::fabs(__x.real()) >= std::numeric_limits<_Tp>::digits
would be sufficient.
Now for the imaginary part, when
std::fabs(__x.imag()) > std::numeric_limits<_Tp>::max() / 2
we will need to expand double-angle formulas to compute in __x.imag()
only. In that case, I would use the following formula to reduce the cancellation:
And use the original formula/implementation for the default case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think an approach similar to #99677 would be better. That way we avoid a bunch of code and most likely get a better implementation.
I don't know when it'll land, but I'm also not touching |
Ah, you're editing |
So I assume we can do the same for |
Hmm, it seems I can delete that complexity and adjust the tests, but it won't be transparent, thanks to the clang-format changes. |
Yes, I'll do that in a separate PR. |
Currently blocked by #122391. |
By attempting to compute
sin(2x)
andcos(2x)
, we were inadvertently producingcomplex<float>(NaN, NaN)
for any|x| > 2^127
, since2x
would be computed asinf
, andsin(inf) == cos(inf) == NaN
.This commit relies on trig identities to sidestep this issue and always produce a valid answer for large values of
tanh
. The test cases only handleddouble
, so they have been expanded to testfloat
andlong double
as well.