Warning: This file is not a C or C++ file. It does not have highlighting.
1 | //===-- Nearest integer floating-point operations ---------------*- 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_NEARESTINTEGEROPERATIONS_H |
10 | #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_NEARESTINTEGEROPERATIONS_H |
11 | |
12 | #include "FEnvImpl.h" |
13 | #include "FPBits.h" |
14 | #include "rounding_mode.h" |
15 | |
16 | #include "hdr/math_macros.h" |
17 | #include "src/__support/CPP/type_traits.h" |
18 | #include "src/__support/common.h" |
19 | #include "src/__support/macros/config.h" |
20 | |
21 | namespace LIBC_NAMESPACE_DECL { |
22 | namespace fputil { |
23 | |
24 | template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
25 | LIBC_INLINE T trunc(T x) { |
26 | using StorageType = typename FPBits<T>::StorageType; |
27 | FPBits<T> bits(x); |
28 | |
29 | // If x is infinity or NaN, return it. |
30 | // If it is zero also we should return it as is, but the logic |
31 | // later in this function takes care of it. But not doing a zero |
32 | // check, we improve the run time of non-zero values. |
33 | if (bits.is_inf_or_nan()) |
34 | return x; |
35 | |
36 | int exponent = bits.get_exponent(); |
37 | |
38 | // If the exponent is greater than the most negative mantissa |
39 | // exponent, then x is already an integer. |
40 | if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN)) |
41 | return x; |
42 | |
43 | // If the exponent is such that abs(x) is less than 1, then return 0. |
44 | if (exponent <= -1) |
45 | return FPBits<T>::zero(bits.sign()).get_val(); |
46 | |
47 | int trim_size = FPBits<T>::FRACTION_LEN - exponent; |
48 | StorageType trunc_mantissa = |
49 | static_cast<StorageType>((bits.get_mantissa() >> trim_size) << trim_size); |
50 | bits.set_mantissa(trunc_mantissa); |
51 | return bits.get_val(); |
52 | } |
53 | |
54 | template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
55 | LIBC_INLINE T ceil(T x) { |
56 | using StorageType = typename FPBits<T>::StorageType; |
57 | FPBits<T> bits(x); |
58 | |
59 | // If x is infinity NaN or zero, return it. |
60 | if (bits.is_inf_or_nan() || bits.is_zero()) |
61 | return x; |
62 | |
63 | bool is_neg = bits.is_neg(); |
64 | int exponent = bits.get_exponent(); |
65 | |
66 | // If the exponent is greater than the most negative mantissa |
67 | // exponent, then x is already an integer. |
68 | if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN)) |
69 | return x; |
70 | |
71 | if (exponent <= -1) { |
72 | if (is_neg) |
73 | return T(-0.0); |
74 | else |
75 | return T(1.0); |
76 | } |
77 | |
78 | uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent; |
79 | StorageType x_u = bits.uintval(); |
80 | StorageType trunc_u = |
81 | static_cast<StorageType>((x_u >> trim_size) << trim_size); |
82 | |
83 | // If x is already an integer, return it. |
84 | if (trunc_u == x_u) |
85 | return x; |
86 | |
87 | bits.set_uintval(trunc_u); |
88 | T trunc_value = bits.get_val(); |
89 | |
90 | // If x is negative, the ceil operation is equivalent to the trunc operation. |
91 | if (is_neg) |
92 | return trunc_value; |
93 | |
94 | return trunc_value + T(1.0); |
95 | } |
96 | |
97 | template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
98 | LIBC_INLINE T floor(T x) { |
99 | FPBits<T> bits(x); |
100 | if (bits.is_neg()) { |
101 | return -ceil(-x); |
102 | } else { |
103 | return trunc(x); |
104 | } |
105 | } |
106 | |
107 | template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0> |
108 | LIBC_INLINE T round(T x) { |
109 | using StorageType = typename FPBits<T>::StorageType; |
110 | FPBits<T> bits(x); |
111 | |
112 | // If x is infinity NaN or zero, return it. |
113 | if (bits.is_inf_or_nan() || bits.is_zero()) |
114 | return x; |
115 | |
116 | int exponent = bits.get_exponent(); |
117 | |
118 | // If the exponent is greater than the most negative mantissa |
119 | // exponent, then x is already an integer. |
120 | if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN)) |
121 | return x; |
122 | |
123 | if (exponent == -1) { |
124 | // Absolute value of x is greater than equal to 0.5 but less than 1. |
125 | return FPBits<T>::one(bits.sign()).get_val(); |
126 | } |
127 | |
128 | if (exponent <= -2) { |
129 | // Absolute value of x is less than 0.5. |
130 | return FPBits<T>::zero(bits.sign()).get_val(); |
131 | } |
132 | |
133 | uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent; |
134 | bool half_bit_set = |
135 | bool(bits.get_mantissa() & (StorageType(1) << (trim_size - 1))); |
136 | StorageType x_u = bits.uintval(); |
137 | StorageType trunc_u = |
138 | static_cast<StorageType>((x_u >> trim_size) << trim_size); |
139 | |
140 | // If x is already an integer, return it. |
141 | if (trunc_u == x_u) |
142 | return x; |
143 | |
144 | bits.set_uintval(trunc_u); |
145 | T trunc_value = bits.get_val(); |
146 | |
147 | if (!half_bit_set) { |
148 | // Franctional part is less than 0.5 so round value is the |
149 | // same as the trunc value. |
150 | return trunc_value; |
151 | } else { |
152 | return bits.is_neg() ? trunc_value - T(1.0) : trunc_value + T(1.0); |
153 | } |
154 | } |
155 | |
156 | template <typename T> |
157 | LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T> |
158 | round_using_specific_rounding_mode(T x, int rnd) { |
159 | using StorageType = typename FPBits<T>::StorageType; |
160 | FPBits<T> bits(x); |
161 | |
162 | // If x is infinity NaN or zero, return it. |
163 | if (bits.is_inf_or_nan() || bits.is_zero()) |
164 | return x; |
165 | |
166 | bool is_neg = bits.is_neg(); |
167 | int exponent = bits.get_exponent(); |
168 | |
169 | // If the exponent is greater than the most negative mantissa |
170 | // exponent, then x is already an integer. |
171 | if (exponent >= static_cast<int>(FPBits<T>::FRACTION_LEN)) |
172 | return x; |
173 | |
174 | if (exponent <= -1) { |
175 | switch (rnd) { |
176 | case FP_INT_DOWNWARD: |
177 | return is_neg ? T(-1.0) : T(0.0); |
178 | case FP_INT_UPWARD: |
179 | return is_neg ? T(-0.0) : T(1.0); |
180 | case FP_INT_TOWARDZERO: |
181 | return is_neg ? T(-0.0) : T(0.0); |
182 | case FP_INT_TONEARESTFROMZERO: |
183 | if (exponent < -1) |
184 | return is_neg ? T(-0.0) : T(0.0); // abs(x) < 0.5 |
185 | return is_neg ? T(-1.0) : T(1.0); // abs(x) >= 0.5 |
186 | case FP_INT_TONEAREST: |
187 | default: |
188 | if (exponent <= -2 || bits.get_mantissa() == 0) |
189 | return is_neg ? T(-0.0) : T(0.0); // abs(x) <= 0.5 |
190 | else |
191 | return is_neg ? T(-1.0) : T(1.0); // abs(x) > 0.5 |
192 | } |
193 | } |
194 | |
195 | uint32_t trim_size = FPBits<T>::FRACTION_LEN - exponent; |
196 | StorageType x_u = bits.uintval(); |
197 | StorageType trunc_u = |
198 | static_cast<StorageType>((x_u >> trim_size) << trim_size); |
199 | |
200 | // If x is already an integer, return it. |
201 | if (trunc_u == x_u) |
202 | return x; |
203 | |
204 | FPBits<T> new_bits(trunc_u); |
205 | T trunc_value = new_bits.get_val(); |
206 | |
207 | StorageType trim_value = |
208 | bits.get_mantissa() & |
209 | static_cast<StorageType>(((StorageType(1) << trim_size) - 1)); |
210 | StorageType half_value = |
211 | static_cast<StorageType>((StorageType(1) << (trim_size - 1))); |
212 | // If exponent is 0, trimSize will be equal to the mantissa width, and |
213 | // truncIsOdd` will not be correct. So, we handle it as a special case |
214 | // below. |
215 | StorageType trunc_is_odd = |
216 | new_bits.get_mantissa() & (StorageType(1) << trim_size); |
217 | |
218 | switch (rnd) { |
219 | case FP_INT_DOWNWARD: |
220 | return is_neg ? trunc_value - T(1.0) : trunc_value; |
221 | case FP_INT_UPWARD: |
222 | return is_neg ? trunc_value : trunc_value + T(1.0); |
223 | case FP_INT_TOWARDZERO: |
224 | return trunc_value; |
225 | case FP_INT_TONEARESTFROMZERO: |
226 | if (trim_value >= half_value) |
227 | return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0); |
228 | return trunc_value; |
229 | case FP_INT_TONEAREST: |
230 | default: |
231 | if (trim_value > half_value) { |
232 | return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0); |
233 | } else if (trim_value == half_value) { |
234 | if (exponent == 0) |
235 | return is_neg ? T(-2.0) : T(2.0); |
236 | if (trunc_is_odd) |
237 | return is_neg ? trunc_value - T(1.0) : trunc_value + T(1.0); |
238 | else |
239 | return trunc_value; |
240 | } else { |
241 | return trunc_value; |
242 | } |
243 | } |
244 | } |
245 | |
246 | template <typename T> |
247 | LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> |
248 | round_using_current_rounding_mode(T x) { |
249 | int rounding_mode = quick_get_round(); |
250 | |
251 | switch (rounding_mode) { |
252 | case FE_DOWNWARD: |
253 | return round_using_specific_rounding_mode(x, FP_INT_DOWNWARD); |
254 | case FE_UPWARD: |
255 | return round_using_specific_rounding_mode(x, FP_INT_UPWARD); |
256 | case FE_TOWARDZERO: |
257 | return round_using_specific_rounding_mode(x, FP_INT_TOWARDZERO); |
258 | case FE_TONEAREST: |
259 | return round_using_specific_rounding_mode(x, FP_INT_TONEAREST); |
260 | default: |
261 | __builtin_unreachable(); |
262 | } |
263 | } |
264 | |
265 | template <bool IsSigned, typename T> |
266 | LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T> |
267 | fromfp(T x, int rnd, unsigned int width) { |
268 | using StorageType = typename FPBits<T>::StorageType; |
269 | |
270 | constexpr StorageType EXPLICIT_BIT = |
271 | FPBits<T>::SIG_MASK - FPBits<T>::FRACTION_MASK; |
272 | |
273 | if (width == 0U) { |
274 | raise_except_if_required(FE_INVALID); |
275 | return FPBits<T>::quiet_nan().get_val(); |
276 | } |
277 | |
278 | FPBits<T> bits(x); |
279 | |
280 | if (bits.is_inf_or_nan()) { |
281 | raise_except_if_required(FE_INVALID); |
282 | return FPBits<T>::quiet_nan().get_val(); |
283 | } |
284 | |
285 | T rounded_value = round_using_specific_rounding_mode(x, rnd); |
286 | |
287 | if constexpr (IsSigned) { |
288 | // T can't hold a finite number >= 2.0 * 2^EXP_BIAS. |
289 | if (width - 1 > FPBits<T>::EXP_BIAS) |
290 | return rounded_value; |
291 | |
292 | StorageType range_exp = |
293 | static_cast<StorageType>(width - 1 + FPBits<T>::EXP_BIAS); |
294 | // rounded_value < -2^(width - 1) |
295 | T range_min = |
296 | FPBits<T>::create_value(Sign::NEG, range_exp, EXPLICIT_BIT).get_val(); |
297 | if (rounded_value < range_min) { |
298 | raise_except_if_required(FE_INVALID); |
299 | return FPBits<T>::quiet_nan().get_val(); |
300 | } |
301 | // rounded_value > 2^(width - 1) - 1 |
302 | T range_max = |
303 | FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() - |
304 | T(1.0); |
305 | if (rounded_value > range_max) { |
306 | raise_except_if_required(FE_INVALID); |
307 | return FPBits<T>::quiet_nan().get_val(); |
308 | } |
309 | |
310 | return rounded_value; |
311 | } |
312 | |
313 | if (rounded_value < T(0.0)) { |
314 | raise_except_if_required(FE_INVALID); |
315 | return FPBits<T>::quiet_nan().get_val(); |
316 | } |
317 | |
318 | // T can't hold a finite number >= 2.0 * 2^EXP_BIAS. |
319 | if (width > FPBits<T>::EXP_BIAS) |
320 | return rounded_value; |
321 | |
322 | StorageType range_exp = static_cast<StorageType>(width + FPBits<T>::EXP_BIAS); |
323 | // rounded_value > 2^width - 1 |
324 | T range_max = |
325 | FPBits<T>::create_value(Sign::POS, range_exp, EXPLICIT_BIT).get_val() - |
326 | T(1.0); |
327 | if (rounded_value > range_max) { |
328 | raise_except_if_required(FE_INVALID); |
329 | return FPBits<T>::quiet_nan().get_val(); |
330 | } |
331 | |
332 | return rounded_value; |
333 | } |
334 | |
335 | template <bool IsSigned, typename T> |
336 | LIBC_INLINE constexpr cpp::enable_if_t<cpp::is_floating_point_v<T>, T> |
337 | fromfpx(T x, int rnd, unsigned int width) { |
338 | T rounded_value = fromfp<IsSigned>(x, rnd, width); |
339 | FPBits<T> bits(rounded_value); |
340 | |
341 | if (!bits.is_nan() && rounded_value != x) |
342 | raise_except_if_required(FE_INEXACT); |
343 | |
344 | return rounded_value; |
345 | } |
346 | |
347 | namespace internal { |
348 | |
349 | template <typename FloatType, typename IntType, |
350 | cpp::enable_if_t<cpp::is_floating_point_v<FloatType> && |
351 | cpp::is_integral_v<IntType>, |
352 | int> = 0> |
353 | LIBC_INLINE IntType rounded_float_to_signed_integer(FloatType x) { |
354 | constexpr IntType INTEGER_MIN = (IntType(1) << (sizeof(IntType) * 8 - 1)); |
355 | constexpr IntType INTEGER_MAX = -(INTEGER_MIN + 1); |
356 | FPBits<FloatType> bits(x); |
357 | auto set_domain_error_and_raise_invalid = []() { |
358 | set_errno_if_required(EDOM); |
359 | raise_except_if_required(FE_INVALID); |
360 | }; |
361 | |
362 | if (bits.is_inf_or_nan()) { |
363 | set_domain_error_and_raise_invalid(); |
364 | return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX; |
365 | } |
366 | |
367 | int exponent = bits.get_exponent(); |
368 | constexpr int EXPONENT_LIMIT = sizeof(IntType) * 8 - 1; |
369 | if (exponent > EXPONENT_LIMIT) { |
370 | set_domain_error_and_raise_invalid(); |
371 | return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX; |
372 | } else if (exponent == EXPONENT_LIMIT) { |
373 | if (bits.is_pos() || bits.get_mantissa() != 0) { |
374 | set_domain_error_and_raise_invalid(); |
375 | return bits.is_neg() ? INTEGER_MIN : INTEGER_MAX; |
376 | } |
377 | // If the control reaches here, then it means that the rounded |
378 | // value is the most negative number for the signed integer type IntType. |
379 | } |
380 | |
381 | // For all other cases, if `x` can fit in the integer type `IntType`, |
382 | // we just return `x`. static_cast will convert the floating |
383 | // point value to the exact integer value. |
384 | return static_cast<IntType>(x); |
385 | } |
386 | |
387 | } // namespace internal |
388 | |
389 | template <typename FloatType, typename IntType, |
390 | cpp::enable_if_t<cpp::is_floating_point_v<FloatType> && |
391 | cpp::is_integral_v<IntType>, |
392 | int> = 0> |
393 | LIBC_INLINE IntType round_to_signed_integer(FloatType x) { |
394 | return internal::rounded_float_to_signed_integer<FloatType, IntType>( |
395 | round(x)); |
396 | } |
397 | |
398 | template <typename FloatType, typename IntType, |
399 | cpp::enable_if_t<cpp::is_floating_point_v<FloatType> && |
400 | cpp::is_integral_v<IntType>, |
401 | int> = 0> |
402 | LIBC_INLINE IntType |
403 | round_to_signed_integer_using_current_rounding_mode(FloatType x) { |
404 | return internal::rounded_float_to_signed_integer<FloatType, IntType>( |
405 | round_using_current_rounding_mode(x)); |
406 | } |
407 | |
408 | } // namespace fputil |
409 | } // namespace LIBC_NAMESPACE_DECL |
410 | |
411 | #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_NEARESTINTEGEROPERATIONS_H |
412 |
Warning: This file is not a C or C++ file. It does not have highlighting.