1//===-- Implementation of tanbf16 function --------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLVM_LIBC_SRC___SUPPORT_MATH_TANBF16_H
10#define LLVM_LIBC_SRC___SUPPORT_MATH_TANBF16_H
11
12#include "hdr/errno_macros.h"
13#include "hdr/fenv_macros.h"
14#include "sincosf_utils.h"
15#include "src/__support/FPUtil/FEnvImpl.h"
16#include "src/__support/FPUtil/FPBits.h"
17#include "src/__support/FPUtil/bfloat16.h"
18#include "src/__support/FPUtil/cast.h"
19#include "src/__support/FPUtil/multiply_add.h"
20#include "src/__support/macros/optimization.h"
21
22namespace LIBC_NAMESPACE_DECL {
23
24namespace math {
25
26LIBC_INLINE bfloat16 tanbf16(bfloat16 x) {
27
28 using FPBits = fputil::FPBits<bfloat16>;
29 FPBits xbits(x);
30
31 uint16_t x_u = xbits.uintval();
32 uint16_t x_abs = x_u & 0x7fff;
33 float xf = x;
34
35 // NaN or -/+ INF
36 if (x_abs >= 0x7F80) {
37 // NaN
38 if (xbits.is_nan()) {
39 if (xbits.is_signaling_nan()) {
40 fputil::raise_except_if_required(FE_INVALID);
41 return FPBits::quiet_nan().get_val();
42 }
43 return x;
44 }
45 //|x| is +/- INF
46 fputil::set_errno_if_required(EDOM);
47 fputil::raise_except_if_required(FE_INVALID);
48 return x + FPBits::quiet_nan().get_val();
49 }
50
51 // Through Exhaustive testing -
52 // The last value where tan(x) ~ x is 0x3db8
53 if (LIBC_UNLIKELY(x_abs <= 0x3db8)) {
54 // |x| = {0}
55 if (LIBC_UNLIKELY(x_abs == 0)) {
56 return x;
57 }
58 return fputil::cast<bfloat16>(x: fputil::multiply_add(x: xf, y: 0x1.0p-11f, z: xf));
59 }
60
61 double xd = static_cast<double>(xf);
62 uint32_t x_abs_f = fputil::FPBits<float>(xf).uintval() & 0x7fffffff;
63 double sin_k, cos_k, sin_y, cosm1_y;
64
65 // TODO: Use bfloat16 version for inv_trigf_utils_internals after they are
66 // available
67 // Tracking issue :
68 // https://github.com/llvm/llvm-project/issues/202079
69 sincosf_utils_internal::sincosf_eval(xd, x_abs: x_abs_f, sin_k, cos_k, sin_y,
70 cosm1_y);
71
72 return fputil::cast<bfloat16>(
73 x: fputil::multiply_add(x: sin_y, y: cos_k,
74 z: fputil::multiply_add(x: cosm1_y, y: sin_k, z: sin_k)) /
75 fputil::multiply_add(x: sin_y, y: -sin_k,
76 z: fputil::multiply_add(x: cosm1_y, y: cos_k, z: cos_k)));
77}
78
79} // namespace math
80
81} // namespace LIBC_NAMESPACE_DECL
82
83#endif // LLVM_LIBC_SRC___SUPPORT_MATH_TANBF16_H
84