1//===-- Implementation header for coshf16 -----------------------*- C++ -*-===//
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_COSHF16_H
10#define LLVM_LIBC_SRC___SUPPORT_MATH_COSHF16_H
11
12#include "include/llvm-libc-macros/float16-macros.h"
13
14#ifdef LIBC_TYPES_HAS_FLOAT16
15
16#include "expxf16_utils.h"
17#include "src/__support/FPUtil/FEnvImpl.h"
18#include "src/__support/FPUtil/FPBits.h"
19#include "src/__support/FPUtil/except_value_utils.h"
20#include "src/__support/FPUtil/rounding_mode.h"
21#include "src/__support/macros/config.h"
22#include "src/__support/macros/optimization.h"
23
24namespace LIBC_NAMESPACE_DECL {
25
26namespace math {
27
28LIBC_INLINE constexpr float16 coshf16(float16 x) {
29
30#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
31 constexpr fputil::ExceptValues<float16, 9> COSHF16_EXCEPTS_POS = {.values: {
32 // x = 0x1.6ap-5, coshf16(x) = 0x1p+0 (RZ)
33 {.input: 0x29a8U, .rnd_towardzero_result: 0x3c00U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
34 // x = 0x1.8c4p+0, coshf16(x) = 0x1.3a8p+1 (RZ)
35 {.input: 0x3e31U, .rnd_towardzero_result: 0x40eaU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
36 // x = 0x1.994p+0, coshf16(x) = 0x1.498p+1 (RZ)
37 {.input: 0x3e65U, .rnd_towardzero_result: 0x4126U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
38 // x = 0x1.b6p+0, coshf16(x) = 0x1.6d8p+1 (RZ)
39 {.input: 0x3ed8U, .rnd_towardzero_result: 0x41b6U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
40 // x = 0x1.aap+1, coshf16(x) = 0x1.be8p+3 (RZ)
41 {.input: 0x42a8U, .rnd_towardzero_result: 0x4afaU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
42 // x = 0x1.cc4p+1, coshf16(x) = 0x1.23cp+4 (RZ)
43 {.input: 0x4331U, .rnd_towardzero_result: 0x4c8fU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
44 // x = 0x1.288p+2, coshf16(x) = 0x1.9b4p+5 (RZ)
45 {.input: 0x44a2U, .rnd_towardzero_result: 0x526dU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
46 // x = 0x1.958p+2, coshf16(x) = 0x1.1a4p+8 (RZ)
47 {.input: 0x4656U, .rnd_towardzero_result: 0x5c69U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
48 // x = 0x1.5fp+3, coshf16(x) = 0x1.c54p+14 (RZ)
49 {.input: 0x497cU, .rnd_towardzero_result: 0x7715U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
50 }};
51
52 constexpr fputil::ExceptValues<float16, 6> COSHF16_EXCEPTS_NEG = {.values: {
53 // x = -0x1.6ap-5, coshf16(x) = 0x1p+0 (RZ)
54 {.input: 0xa9a8U, .rnd_towardzero_result: 0x3c00U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
55 // x = -0x1.b6p+0, coshf16(x) = 0x1.6d8p+1 (RZ)
56 {.input: 0xbed8U, .rnd_towardzero_result: 0x41b6U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
57 // x = -0x1.288p+2, coshf16(x) = 0x1.9b4p+5 (RZ)
58 {.input: 0xc4a2U, .rnd_towardzero_result: 0x526dU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
59 // x = -0x1.5fp+3, coshf16(x) = 0x1.c54p+14 (RZ)
60 {.input: 0xc97cU, .rnd_towardzero_result: 0x7715U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 1U},
61 // x = -0x1.8c4p+0, coshf16(x) = 0x1.3a8p+1 (RZ)
62 {.input: 0xbe31U, .rnd_towardzero_result: 0x40eaU, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
63 // x = -0x1.994p+0, coshf16(x) = 0x1.498p+1 (RZ)
64 {.input: 0xbe65U, .rnd_towardzero_result: 0x4126U, .rnd_upward_offset: 1U, .rnd_downward_offset: 0U, .rnd_tonearest_offset: 0U},
65 }};
66#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
67
68 using namespace expxf16_internal;
69 using FPBits = fputil::FPBits<float16>;
70 FPBits x_bits(x);
71
72 uint16_t x_u = x_bits.uintval();
73 uint16_t x_abs = x_u & 0x7fffU;
74
75 // When |x| >= acosh(2^16), or x is NaN.
76 if (LIBC_UNLIKELY(x_abs >= 0x49e5U)) {
77 // cosh(NaN) = NaN
78 if (x_bits.is_nan()) {
79 if (x_bits.is_signaling_nan()) {
80 fputil::raise_except_if_required(FE_INVALID);
81 return FPBits::quiet_nan().get_val();
82 }
83
84 return x;
85 }
86
87 // When |x| >= acosh(2^16).
88 if (x_abs >= 0x49e5U) {
89 // cosh(+/-inf) = +inf
90 if (x_bits.is_inf())
91 return FPBits::inf().get_val();
92
93#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
94 fputil::set_errno_if_required(ERANGE);
95 fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
96 return FPBits::inf().get_val();
97#else
98 switch (fputil::quick_get_round()) {
99 case FE_TONEAREST:
100 case FE_UPWARD:
101 fputil::set_errno_if_required(ERANGE);
102 fputil::raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
103 return FPBits::inf().get_val();
104 default:
105 return FPBits::max_normal().get_val();
106 }
107#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
108 }
109 }
110
111#ifndef LIBC_MATH_HAS_SKIP_ACCURATE_PASS
112 if (x_bits.is_pos()) {
113 if (auto r = COSHF16_EXCEPTS_POS.lookup(x_bits: x_u); LIBC_UNLIKELY(r.has_value()))
114 return r.value();
115 } else {
116 if (auto r = COSHF16_EXCEPTS_NEG.lookup(x_bits: x_u); LIBC_UNLIKELY(r.has_value()))
117 return r.value();
118 }
119#endif // !LIBC_MATH_HAS_SKIP_ACCURATE_PASS
120
121 return eval_sinh_or_cosh</*IsSinh=*/false>(x);
122}
123
124} // namespace math
125
126} // namespace LIBC_NAMESPACE_DECL
127
128#endif // LIBC_TYPES_HAS_FLOAT16
129
130#endif // LLVM_LIBC_SRC___SUPPORT_MATH_COSHF16_H
131