1 | //===-- Half-precision sinh(x) 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 | #include "src/math/sinhf16.h" |
10 | #include "expxf16.h" |
11 | #include "hdr/errno_macros.h" |
12 | #include "hdr/fenv_macros.h" |
13 | #include "src/__support/FPUtil/FEnvImpl.h" |
14 | #include "src/__support/FPUtil/FPBits.h" |
15 | #include "src/__support/FPUtil/except_value_utils.h" |
16 | #include "src/__support/FPUtil/rounding_mode.h" |
17 | #include "src/__support/common.h" |
18 | #include "src/__support/macros/config.h" |
19 | #include "src/__support/macros/optimization.h" |
20 | |
21 | namespace LIBC_NAMESPACE_DECL { |
22 | |
23 | #ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS |
24 | static constexpr fputil::ExceptValues<float16, 17> SINHF16_EXCEPTS_POS = {{ |
25 | // x = 0x1.714p-5, sinhf16(x) = 0x1.714p-5 (RZ) |
26 | {0x29c5U, 0x29c5U, 1U, 0U, 1U}, |
27 | // x = 0x1.25p-4, sinhf16(x) = 0x1.25p-4 (RZ) |
28 | {0x2c94U, 0x2c94U, 1U, 0U, 1U}, |
29 | // x = 0x1.f5p-4, sinhf16(x) = 0x1.f64p-4 (RZ) |
30 | {0x2fd4U, 0x2fd9U, 1U, 0U, 0U}, |
31 | // x = 0x1.b1cp-3, sinhf16(x) = 0x1.b4cp-3 (RZ) |
32 | {0x32c7U, 0x32d3U, 1U, 0U, 1U}, |
33 | // x = 0x1.6e8p-2, sinhf16(x) = 0x1.764p-2 (RZ) |
34 | {0x35baU, 0x35d9U, 1U, 0U, 1U}, |
35 | // x = 0x1.6b4p-1, sinhf16(x) = 0x1.8a4p-1 (RZ) |
36 | {0x39adU, 0x3a29U, 1U, 0U, 1U}, |
37 | // x = 0x1.a58p-1, sinhf16(x) = 0x1.d68p-1 (RZ) |
38 | {0x3a96U, 0x3b5aU, 1U, 0U, 1U}, |
39 | // x = 0x1.574p+0, sinhf16(x) = 0x1.c78p+0 (RZ) |
40 | {0x3d5dU, 0x3f1eU, 1U, 0U, 1U}, |
41 | // x = 0x1.648p+1, sinhf16(x) = 0x1.024p+3 (RZ) |
42 | {0x4192U, 0x4809U, 1U, 0U, 0U}, |
43 | // x = 0x1.cdcp+1, sinhf16(x) = 0x1.26cp+4 (RZ) |
44 | {0x4337U, 0x4c9bU, 1U, 0U, 0U}, |
45 | // x = 0x1.d0cp+1, sinhf16(x) = 0x1.2d8p+4 (RZ) |
46 | {0x4343U, 0x4cb6U, 1U, 0U, 1U}, |
47 | // x = 0x1.018p+2, sinhf16(x) = 0x1.bfp+4 (RZ) |
48 | {0x4406U, 0x4efcU, 1U, 0U, 0U}, |
49 | // x = 0x1.2fcp+2, sinhf16(x) = 0x1.cc4p+5 (RZ) |
50 | {0x44bfU, 0x5331U, 1U, 0U, 1U}, |
51 | // x = 0x1.4ecp+2, sinhf16(x) = 0x1.75cp+6 (RZ) |
52 | {0x453bU, 0x55d7U, 1U, 0U, 0U}, |
53 | // x = 0x1.8a4p+2, sinhf16(x) = 0x1.d94p+7 (RZ) |
54 | {0x4629U, 0x5b65U, 1U, 0U, 1U}, |
55 | // x = 0x1.5fp+3, sinhf16(x) = 0x1.c54p+14 (RZ) |
56 | {0x497cU, 0x7715U, 1U, 0U, 1U}, |
57 | // x = 0x1.3c8p+1, sinhf16(x) = 0x1.78ap+2 (RZ) |
58 | {0x40f2U, 0x45e2U, 1U, 0U, 1U}, |
59 | }}; |
60 | |
61 | static constexpr fputil::ExceptValues<float16, 13> SINHF16_EXCEPTS_NEG = {{ |
62 | // x = -0x1.714p-5, sinhf16(x) = -0x1.714p-5 (RZ) |
63 | {0xa9c5U, 0xa9c5U, 0U, 1U, 1U}, |
64 | // x = -0x1.25p-4, sinhf16(x) = -0x1.25p-4 (RZ) |
65 | {0xac94U, 0xac94U, 0U, 1U, 1U}, |
66 | // x = -0x1.f5p-4, sinhf16(x) = -0x1.f64p-4 (RZ) |
67 | {0xafd4U, 0xafd9U, 0U, 1U, 0U}, |
68 | // x = -0x1.6e8p-2, sinhf16(x) = -0x1.764p-2 (RZ) |
69 | {0xb5baU, 0xb5d9U, 0U, 1U, 1U}, |
70 | // x = -0x1.a58p-1, sinhf16(x) = -0x1.d68p-1 (RZ) |
71 | {0xba96U, 0xbb5aU, 0U, 1U, 1U}, |
72 | // x = -0x1.cdcp+1, sinhf16(x) = -0x1.26cp+4 (RZ) |
73 | {0xc337U, 0xcc9bU, 0U, 1U, 0U}, |
74 | // x = -0x1.d0cp+1, sinhf16(x) = -0x1.2d8p+4 (RZ) |
75 | {0xc343U, 0xccb6U, 0U, 1U, 1U}, |
76 | // x = -0x1.018p+2, sinhf16(x) = -0x1.bfp+4 (RZ) |
77 | {0xc406U, 0xcefcU, 0U, 1U, 0U}, |
78 | // x = -0x1.2fcp+2, sinhf16(x) = -0x1.cc4p+5 (RZ) |
79 | {0xc4bfU, 0xd331U, 0U, 1U, 1U}, |
80 | // x = -0x1.4ecp+2, sinhf16(x) = -0x1.75cp+6 (RZ) |
81 | {0xc53bU, 0xd5d7U, 0U, 1U, 0U}, |
82 | // x = -0x1.8a4p+2, sinhf16(x) = -0x1.d94p+7 (RZ) |
83 | {0xc629U, 0xdb65U, 0U, 1U, 1U}, |
84 | // x = -0x1.5fp+3, sinhf16(x) = -0x1.c54p+14 (RZ) |
85 | {0xc97cU, 0xf715U, 0U, 1U, 1U}, |
86 | // x = -0x1.3c8p+1, sinhf16(x) = -0x1.78ap+2 (RZ) |
87 | {0xc0f2U, 0xc5e2U, 0U, 1U, 1U}, |
88 | }}; |
89 | #endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS |
90 | |
91 | LLVM_LIBC_FUNCTION(float16, sinhf16, (float16 x)) { |
92 | using FPBits = fputil::FPBits<float16>; |
93 | FPBits x_bits(x); |
94 | |
95 | uint16_t x_u = x_bits.uintval(); |
96 | uint16_t x_abs = x_u & 0x7fffU; |
97 | |
98 | // When |x| = 0, or -2^(-14) <= x <= -2^(-9), or |x| >= asinh(2^16), or x is |
99 | // NaN. |
100 | if (LIBC_UNLIKELY(x_abs == 0U || (x_u >= 0x8400U && x_u <= 0xa400U) || |
101 | x_abs >= 0x49e5U)) { |
102 | // sinh(NaN) = NaN |
103 | if (x_bits.is_nan()) { |
104 | if (x_bits.is_signaling_nan()) { |
105 | fputil::raise_except_if_required(FE_INVALID); |
106 | return FPBits::quiet_nan().get_val(); |
107 | } |
108 | |
109 | return x; |
110 | } |
111 | |
112 | // sinh(+/-0) = sinh(+/-0) |
113 | if (x_abs == 0U) |
114 | return FPBits::zero(x_bits.sign()).get_val(); |
115 | |
116 | // When |x| >= asinh(2^16). |
117 | if (x_abs >= 0x49e5U) { |
118 | // sinh(+/-inf) = +/-inf |
119 | if (x_bits.is_inf()) |
120 | return FPBits::inf(x_bits.sign()).get_val(); |
121 | |
122 | int rounding_mode = fputil::quick_get_round(); |
123 | if (rounding_mode == FE_TONEAREST || |
124 | (x_bits.is_pos() && rounding_mode == FE_UPWARD) || |
125 | (x_bits.is_neg() && rounding_mode == FE_DOWNWARD)) { |
126 | fputil::set_errno_if_required(ERANGE); |
127 | fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT); |
128 | return FPBits::inf(x_bits.sign()).get_val(); |
129 | } |
130 | return FPBits::max_normal(x_bits.sign()).get_val(); |
131 | } |
132 | |
133 | // When -2^(-14) <= x <= -2^(-9). |
134 | if (fputil::fenv_is_round_down()) |
135 | return FPBits(static_cast<uint16_t>(x_u + 1)).get_val(); |
136 | return FPBits(static_cast<uint16_t>(x_u)).get_val(); |
137 | } |
138 | |
139 | #ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS |
140 | if (x_bits.is_pos()) { |
141 | if (auto r = SINHF16_EXCEPTS_POS.lookup(x_u); LIBC_UNLIKELY(r.has_value())) |
142 | return r.value(); |
143 | } else { |
144 | if (auto r = SINHF16_EXCEPTS_NEG.lookup(x_u); LIBC_UNLIKELY(r.has_value())) |
145 | return r.value(); |
146 | } |
147 | #endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS |
148 | |
149 | return eval_sinh_or_cosh</*IsSinh=*/true>(x); |
150 | } |
151 | |
152 | } // namespace LIBC_NAMESPACE_DECL |
153 | |