• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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