xref: /llvm-project/libc/src/__support/FPUtil/except_value_utils.h (revision ed199c8d7668c14e9e9e8e4bfa59793e3f7f0266)
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 
22 namespace LIBC_NAMESPACE_DECL {
23 
24 namespace 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 
41 template <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 constexpr cpp::optional<T> lookup(StorageType x_bits) const {
57     for (size_t i = 0; i < N; ++i) {
58       if (LIBC_UNLIKELY(x_bits == values[i].input)) {
59         StorageType out_bits = values[i].rnd_towardzero_result;
60         switch (fputil::quick_get_round()) {
61         case FE_UPWARD:
62           out_bits += values[i].rnd_upward_offset;
63           break;
64         case FE_DOWNWARD:
65           out_bits += values[i].rnd_downward_offset;
66           break;
67         case FE_TONEAREST:
68           out_bits += values[i].rnd_tonearest_offset;
69           break;
70         }
71         return FPBits<T>(out_bits).get_val();
72       }
73     }
74     return cpp::nullopt;
75   }
76 
77   LIBC_INLINE constexpr cpp::optional<T> lookup_odd(StorageType x_abs,
78                                                     bool sign) const {
79     for (size_t i = 0; i < N; ++i) {
80       if (LIBC_UNLIKELY(x_abs == values[i].input)) {
81         StorageType out_bits = values[i].rnd_towardzero_result;
82         switch (fputil::quick_get_round()) {
83         case FE_UPWARD:
84           if (sign)
85             out_bits += values[i].rnd_downward_offset;
86           else
87             out_bits += values[i].rnd_upward_offset;
88           break;
89         case FE_DOWNWARD:
90           // Use conditionals instead of ternary operator to work around gcc's
91           // -Wconversion false positive bug:
92           // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101537
93           if (sign)
94             out_bits += values[i].rnd_upward_offset;
95           else
96             out_bits += values[i].rnd_downward_offset;
97           break;
98         case FE_TONEAREST:
99           out_bits += values[i].rnd_tonearest_offset;
100           break;
101         }
102         T result = FPBits<T>(out_bits).get_val();
103         if (sign)
104           result = -result;
105 
106         return result;
107       }
108     }
109     return cpp::nullopt;
110   }
111 };
112 
113 // Helper functions to set results for exceptional cases.
114 template <typename T> LIBC_INLINE T round_result_slightly_down(T value_rn) {
115   volatile T tmp = value_rn;
116   tmp -= FPBits<T>::min_normal().get_val();
117   return tmp;
118 }
119 
120 template <typename T> LIBC_INLINE T round_result_slightly_up(T value_rn) {
121   volatile T tmp = value_rn;
122   tmp += FPBits<T>::min_normal().get_val();
123   return tmp;
124 }
125 
126 #if defined(LIBC_TYPES_HAS_FLOAT16) &&                                         \
127     !defined(LIBC_TARGET_CPU_HAS_FAST_FLOAT16_OPS)
128 template <> LIBC_INLINE float16 round_result_slightly_down(float16 value_rn) {
129   volatile float tmp = value_rn;
130   tmp -= FPBits<float16>::min_normal().get_val();
131   return cast<float16>(tmp);
132 }
133 
134 template <> LIBC_INLINE float16 round_result_slightly_up(float16 value_rn) {
135   volatile float tmp = value_rn;
136   tmp += FPBits<float16>::min_normal().get_val();
137   return cast<float16>(tmp);
138 }
139 #endif
140 
141 } // namespace fputil
142 
143 } // namespace LIBC_NAMESPACE_DECL
144 
145 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_EXCEPT_VALUE_UTILS_H
146