1//===-- Common header for helpers to set exceptional values -----*- 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_FPUTIL_EXCEPT_VALUE_UTILS_H
10#define LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
11
12#include "FEnvImpl.h"
13#include "FPBits.h"
14#include "cast.h"
15#include "rounding_mode.h"
16#include "src/__support/CPP/optional.h"
17#include "src/__support/macros/config.h"
18#include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
19#include "src/__support/macros/properties/cpu_features.h"
20#include "src/__support/macros/properties/types.h"
21
22namespace LIBC_NAMESPACE_DECL {
23
24namespace fputil {
25
26// This file contains utility functions and classes to manage exceptional values
27// when there are many of them.
28//
29// Example usage:
30//
31// Define list of exceptional inputs and outputs:
32// static constexpr int N = ...; // Number of exceptional values.
33// static constexpr fputil::ExceptValues<StorageType, N> Excepts {
34// <list of input bits, output bits and offsets>
35// };
36//
37// Check for exceptional inputs:
38// if (auto r = Excepts.lookup(x_bits); LIBC_UNLIKELY(r.has_value()))
39// return r.value();
40
41template <typename T, size_t N> struct ExceptValues {
42 static_assert(cpp::is_floating_point_v<T>, "Must be a floating point type.");
43
44 using StorageType = typename FPBits<T>::StorageType;
45
46 struct Mapping {
47 StorageType input;
48 StorageType rnd_towardzero_result;
49 StorageType rnd_upward_offset;
50 StorageType rnd_downward_offset;
51 StorageType rnd_tonearest_offset;
52 };
53
54 Mapping values[N];
55
56 LIBC_INLINE LIBC_CONSTEXPR_DEFAULT cpp::optional<T>
57 lookup(StorageType x_bits) const {
58 for (size_t i = 0; i < N; ++i) {
59 if (LIBC_UNLIKELY(x_bits == values[i].input)) {
60 StorageType out_bits = values[i].rnd_towardzero_result;
61#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
62 out_bits += values[i].rnd_tonearest_offset;
63#else
64 switch (fputil::quick_get_round()) {
65 case FE_UPWARD:
66 out_bits += values[i].rnd_upward_offset;
67 break;
68 case FE_DOWNWARD:
69 out_bits += values[i].rnd_downward_offset;
70 break;
71 case FE_TONEAREST:
72 out_bits += values[i].rnd_tonearest_offset;
73 break;
74 }
75#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
76 return FPBits<T>(out_bits).get_val();
77 }
78 }
79 return cpp::nullopt;
80 }
81
82 LIBC_INLINE LIBC_CONSTEXPR_DEFAULT cpp::optional<T>
83 lookup_odd(StorageType x_abs, bool sign) const {
84 for (size_t i = 0; i < N; ++i) {
85 if (LIBC_UNLIKELY(x_abs == values[i].input)) {
86 StorageType out_bits = values[i].rnd_towardzero_result;
87#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
88 out_bits += values[i].rnd_tonearest_offset;
89#else
90 switch (fputil::quick_get_round()) {
91 case FE_UPWARD:
92 if (sign)
93 out_bits += values[i].rnd_downward_offset;
94 else
95 out_bits += values[i].rnd_upward_offset;
96 break;
97 case FE_DOWNWARD:
98 // Use conditionals instead of ternary operator to work around gcc's
99 // -Wconversion false positive bug:
100 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101537
101 if (sign)
102 out_bits += values[i].rnd_upward_offset;
103 else
104 out_bits += values[i].rnd_downward_offset;
105 break;
106 case FE_TONEAREST:
107 out_bits += values[i].rnd_tonearest_offset;
108 break;
109 }
110#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
111 T result = FPBits<T>(out_bits).get_val();
112 if (sign)
113 result = -result;
114
115 return result;
116 }
117 }
118 return cpp::nullopt;
119 }
120};
121
122// Helper functions to set results for exceptional cases.
123template <typename T>
124LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T round_result_slightly_down(T value_rn) {
125#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
126 fputil::raise_except_if_required(FE_INEXACT);
127 return value_rn;
128#else
129 if (cpp::is_constant_evaluated()) {
130 fputil::raise_except_if_required(FE_INEXACT);
131 return value_rn;
132 } else {
133 volatile T tmp = value_rn;
134 tmp -= FPBits<T>::min_normal().get_val();
135 fputil::raise_except_if_required(FE_INEXACT);
136 return tmp;
137 }
138#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
139}
140
141template <typename T>
142LIBC_INLINE LIBC_CONSTEXPR_DEFAULT T round_result_slightly_up(T value_rn) {
143#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
144 fputil::raise_except_if_required(FE_INEXACT);
145 return value_rn;
146#else
147 if (cpp::is_constant_evaluated()) {
148 fputil::raise_except_if_required(FE_INEXACT);
149 return value_rn;
150 } else {
151 volatile T tmp = value_rn;
152 tmp += FPBits<T>::min_normal().get_val();
153 fputil::raise_except_if_required(FE_INEXACT);
154 return tmp;
155 }
156#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
157}
158
159#if defined(LIBC_TYPES_HAS_FLOAT16) && \
160 !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
161template <>
162LIBC_INLINE LIBC_CONSTEXPR_DEFAULT float16
163round_result_slightly_down(float16 value_rn) {
164#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
165 fputil::raise_except_if_required(FE_INEXACT);
166 return value_rn;
167#else
168 if (cpp::is_constant_evaluated()) {
169 return value_rn;
170 } else {
171 volatile float tmp = value_rn;
172 tmp -= FPBits<float16>::min_normal().get_val();
173 fputil::raise_except_if_required(FE_INEXACT);
174 return cast<float16>(x: tmp);
175 }
176#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
177}
178
179template <>
180LIBC_INLINE LIBC_CONSTEXPR_DEFAULT float16
181round_result_slightly_up(float16 value_rn) {
182#ifdef LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
183 fputil::raise_except_if_required(FE_INEXACT);
184 return value_rn;
185#else
186 if (cpp::is_constant_evaluated()) {
187 return value_rn;
188 } else {
189 volatile float tmp = value_rn;
190 tmp += FPBits<float16>::min_normal().get_val();
191 fputil::raise_except_if_required(FE_INEXACT);
192 return cast<float16>(x: tmp);
193 }
194#endif // LIBC_MATH_HAS_ASSUME_ROUND_NEAREST_ONLY
195}
196#endif
197
198} // namespace fputil
199
200} // namespace LIBC_NAMESPACE_DECL
201
202#endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
203