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