Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -493,22 +493,21 @@ template <class _Tp>
__DPCPP_SYCL_EXTERNAL _SYCL_EXT_CPLX_INLINE_VISIBILITY
typename std::enable_if_t<is_genfloat<_Tp>::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
Expand Down
142 changes: 142 additions & 0 deletions sycl/test/regression/tanh_complex.cpp
Original file line number Diff line number Diff line change
@@ -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 <sycl/ext/oneapi/experimental/complex/complex.hpp>
#include <sycl/sycl.hpp>

#include <complex>
#include <limits>

namespace syclexp = sycl::ext::oneapi::experimental;

int Failures = 0;

template <typename T> 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<T> sycl_res = \
syclexp::tanh(syclexp::complex<T>{REAL, IMAG}); \
std::complex<T> std_res = std::tanh(std::complex<T>{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<T> sycl_res = \
syclexp::tanh(syclexp::complex<T>{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<float>::infinity(), float);
CHECK_TANH_RESULT(32.0, std::numeric_limits<double>::infinity(), double);

CHECK_TANH_RESULT(32.0, -std::numeric_limits<float>::infinity(), float);
CHECK_TANH_RESULT(32.0, -std::numeric_limits<double>::infinity(), double);

CHECK_TANH_RESULT(std::numeric_limits<float>::max(), 0.0, float);
CHECK_TANH_RESULT(std::numeric_limits<double>::max(), 0.0, double);

CHECK_TANH_RESULT(0.0, std::numeric_limits<float>::max(), float);
CHECK_TANH_RESULT(0.0, std::numeric_limits<double>::max(), double);

CHECK_TANH_RESULT(0.0, 0.0, float);
CHECK_TANH_RESULT(0.0, 0.0, double);

CHECK_TANH_RESULT(std::numeric_limits<float>::quiet_NaN(), 1.0, float);
CHECK_TANH_RESULT(std::numeric_limits<double>::quiet_NaN(), 1.0, double);

CHECK_TANH_RESULT(std::numeric_limits<float>::quiet_NaN(),
std::numeric_limits<float>::quiet_NaN(), float);
CHECK_TANH_RESULT(std::numeric_limits<double>::quiet_NaN(),
std::numeric_limits<double>::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<float>::infinity(), 32.0, 1.0, 0.0,
float);
CHECK_TANH_REF_RESULT(std::numeric_limits<double>::infinity(), 32.0, 1.0, 0.0,
double);

CHECK_TANH_REF_RESULT(std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity(), 1, 0.0, float);
CHECK_TANH_REF_RESULT(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(), 1, 0.0,
double);

CHECK_TANH_REF_RESULT(-std::numeric_limits<float>::infinity(), 32.0, -1.0,
0.0, float);
CHECK_TANH_REF_RESULT(-std::numeric_limits<double>::infinity(), 32.0, -1.0,
0.0, double);

CHECK_TANH_REF_RESULT(-std::numeric_limits<float>::infinity(),
-std::numeric_limits<float>::infinity(), -1.0, 0.0,
float);
CHECK_TANH_REF_RESULT(-std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity(), -1.0, 0.0,
double);

CHECK_TANH_REF_RESULT(std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::quiet_NaN(), 1.0, 0.0,
float);
CHECK_TANH_REF_RESULT(std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::quiet_NaN(), 1.0, 0.0,
double);

CHECK_TANH_REF_RESULT(-std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::quiet_NaN(), -1.0, 0.0,
float);
CHECK_TANH_REF_RESULT(-std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::quiet_NaN(), -1.0, 0.0,
double);

CHECK_TANH_REF_RESULT(std::numeric_limits<float>::quiet_NaN(), 0.0,
std::numeric_limits<float>::quiet_NaN(), 0.0, float);
CHECK_TANH_REF_RESULT(std::numeric_limits<double>::quiet_NaN(), 0.0,
std::numeric_limits<float>::quiet_NaN(), 0.0, double);

return Failures;
}
Loading