diff --git a/sycl/include/sycl/ext/oneapi/experimental/complex/detail/complex_math.hpp b/sycl/include/sycl/ext/oneapi/experimental/complex/detail/complex_math.hpp index 0d84fc5060a3f..c9557f83d2093 100644 --- a/sycl/include/sycl/ext/oneapi/experimental/complex/detail/complex_math.hpp +++ b/sycl/include/sycl/ext/oneapi/experimental/complex/detail/complex_math.hpp @@ -493,22 +493,21 @@ template __DPCPP_SYCL_EXTERNAL _SYCL_EXT_CPLX_INLINE_VISIBILITY typename std::enable_if_t::value, complex<_Tp>> tanh(const complex<_Tp> &__x) { - if (sycl::isinf(__x.real())) { - if (!sycl::isfinite(__x.imag())) - return complex<_Tp>(sycl::copysign(_Tp(1), __x.real()), _Tp(0)); - return complex<_Tp>(sycl::copysign(_Tp(1), __x.real()), - sycl::copysign(_Tp(0), sycl::sin(_Tp(2) * __x.imag()))); - } + if (sycl::isinf(__x.real())) + return complex<_Tp>( + sycl::copysign(_Tp(1), __x.real()), + sycl::copysign(_Tp(0), sycl::isfinite(__x.imag()) + ? sycl::sin(_Tp(2) * __x.imag()) + : _Tp(1))); if (sycl::isnan(__x.real()) && __x.imag() == 0) return __x; - _Tp __2r(_Tp(2) * __x.real()); - _Tp __2i(_Tp(2) * __x.imag()); - _Tp __d(sycl::cosh(__2r) + sycl::cos(__2i)); - _Tp __2rsh(sycl::sinh(__2r)); - if (sycl::isinf(__2rsh) && sycl::isinf(__d)) - return complex<_Tp>(__2rsh > _Tp(0) ? _Tp(1) : _Tp(-1), - __2i > _Tp(0) ? _Tp(0) : _Tp(-0.)); - return complex<_Tp>(__2rsh / __d, sycl::sin(__2i) / __d); + complex<_Tp> sinh_x = sinh(__x); + complex<_Tp> cosh_x = cosh(__x); + if (sycl::isinf(sinh_x.real()) && sycl::isinf(cosh_x.real())) + return complex<_Tp>(sinh_x.real() * cosh_x.real() > _Tp(0) ? _Tp(1) + : _Tp(-1), + __x.imag() > _Tp(0) ? _Tp(0) : _Tp(-0.)); + return sinh_x / cosh_x; } // asin diff --git a/sycl/test/regression/tanh_complex.cpp b/sycl/test/regression/tanh_complex.cpp new file mode 100644 index 0000000000000..3396cf0da2004 --- /dev/null +++ b/sycl/test/regression/tanh_complex.cpp @@ -0,0 +1,142 @@ +// RUN: %clangxx -fsycl %s -o %t.out +// RUN: %t.out + +// Checks the results of tanh on certain complex numbers. + +#define SYCL_EXT_ONEAPI_COMPLEX + +#include +#include + +#include +#include + +namespace syclexp = sycl::ext::oneapi::experimental; + +int Failures = 0; + +template bool FloatingPointEq(T LHS, T RHS) { + if (std::isnan(LHS)) + return std::isnan(RHS); + // Allow some rounding differences, but minimal. + return std::abs(LHS - RHS) < T{0.0001}; +} + +#define CHECK_TANH_RESULT(REAL, IMAG, T) \ + { \ + syclexp::complex sycl_res = \ + syclexp::tanh(syclexp::complex{REAL, IMAG}); \ + std::complex std_res = std::tanh(std::complex{REAL, IMAG}); \ + if (!FloatingPointEq(sycl_res.real(), std_res.real())) { \ + std::cout << "Real differ in tanh((" << REAL << ", " << IMAG \ + << ")): " << sycl_res.real() << " != " << std_res.real() \ + << std::endl; \ + ++Failures; \ + } \ + if (!FloatingPointEq(sycl_res.imag(), std_res.imag())) { \ + std::cout << "Imag differ in tanh((" << REAL << ", " << IMAG \ + << ")): " << sycl_res.imag() << " != " << std_res.imag() \ + << std::endl; \ + ++Failures; \ + } \ + } + +#define CHECK_TANH_REF_RESULT(REAL, IMAG, REF_REAL, REF_IMAG, T) \ + { \ + syclexp::complex sycl_res = \ + syclexp::tanh(syclexp::complex{REAL, IMAG}); \ + if (!FloatingPointEq(sycl_res.real(), T{REF_REAL})) { \ + std::cout << "Real differ in tanh((" << REAL << ", " << IMAG \ + << ")): " << sycl_res.real() << " != " << REF_REAL \ + << std::endl; \ + ++Failures; \ + } \ + if (!FloatingPointEq(sycl_res.imag(), T{REF_IMAG})) { \ + std::cout << "Imag differ in tanh((" << REAL << ", " << IMAG \ + << ")): " << sycl_res.imag() << " != " << REF_IMAG \ + << std::endl; \ + ++Failures; \ + } \ + } + +int main() { + // Set precision for easier debugging. + std::cout << std::setprecision(10); + + CHECK_TANH_RESULT(0, -11.0, float); + CHECK_TANH_RESULT(0, -11.0, double); + + CHECK_TANH_RESULT(32.0, std::numeric_limits::infinity(), float); + CHECK_TANH_RESULT(32.0, std::numeric_limits::infinity(), double); + + CHECK_TANH_RESULT(32.0, -std::numeric_limits::infinity(), float); + CHECK_TANH_RESULT(32.0, -std::numeric_limits::infinity(), double); + + CHECK_TANH_RESULT(std::numeric_limits::max(), 0.0, float); + CHECK_TANH_RESULT(std::numeric_limits::max(), 0.0, double); + + CHECK_TANH_RESULT(0.0, std::numeric_limits::max(), float); + CHECK_TANH_RESULT(0.0, std::numeric_limits::max(), double); + + CHECK_TANH_RESULT(0.0, 0.0, float); + CHECK_TANH_RESULT(0.0, 0.0, double); + + CHECK_TANH_RESULT(std::numeric_limits::quiet_NaN(), 1.0, float); + CHECK_TANH_RESULT(std::numeric_limits::quiet_NaN(), 1.0, double); + + CHECK_TANH_RESULT(std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), float); + CHECK_TANH_RESULT(std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), double); + + // The MSVC implementation of tanh for complex numbers does not adhere to the + // following requirements set by the definition of std::tanh: + // * When the input has an infinite real, then the function should return + // (1, +-0). + // * When the input is (NaN, 0), the result should be (NaN, 0). + // Instead we check the results using reference values rather than trusting + // the result of std::tanh in these cases. + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), 32.0, 1.0, 0.0, + float); + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), 32.0, 1.0, 0.0, + double); + + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), + std::numeric_limits::infinity(), 1, 0.0, float); + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), + std::numeric_limits::infinity(), 1, 0.0, + double); + + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), 32.0, -1.0, + 0.0, float); + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), 32.0, -1.0, + 0.0, double); + + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), -1.0, 0.0, + float); + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), + -std::numeric_limits::infinity(), -1.0, 0.0, + double); + + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), 1.0, 0.0, + float); + CHECK_TANH_REF_RESULT(std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), 1.0, 0.0, + double); + + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), -1.0, 0.0, + float); + CHECK_TANH_REF_RESULT(-std::numeric_limits::infinity(), + std::numeric_limits::quiet_NaN(), -1.0, 0.0, + double); + + CHECK_TANH_REF_RESULT(std::numeric_limits::quiet_NaN(), 0.0, + std::numeric_limits::quiet_NaN(), 0.0, float); + CHECK_TANH_REF_RESULT(std::numeric_limits::quiet_NaN(), 0.0, + std::numeric_limits::quiet_NaN(), 0.0, double); + + return Failures; +}