xref: /aosp_15_r20/external/llvm-libc/src/__support/FPUtil/BasicOperations.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
1 //===-- Basic operations on floating point numbers --------------*- 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_BASICOPERATIONS_H
10 #define LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H
11 
12 #include "FEnvImpl.h"
13 #include "FPBits.h"
14 #include "dyadic_float.h"
15 
16 #include "src/__support/CPP/type_traits.h"
17 #include "src/__support/big_int.h"
18 #include "src/__support/common.h"
19 #include "src/__support/macros/config.h"
20 #include "src/__support/macros/optimization.h" // LIBC_UNLIKELY
21 #include "src/__support/macros/properties/architectures.h"
22 #include "src/__support/macros/properties/types.h"
23 #include "src/__support/uint128.h"
24 
25 namespace LIBC_NAMESPACE_DECL {
26 namespace fputil {
27 
28 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
abs(T x)29 LIBC_INLINE T abs(T x) {
30   return FPBits<T>(x).abs().get_val();
31 }
32 
33 namespace internal {
34 
35 template <typename T>
max(T x,T y)36 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> max(T x, T y) {
37   FPBits<T> x_bits(x);
38   FPBits<T> y_bits(y);
39 
40   // To make sure that fmax(+0, -0) == +0 == fmax(-0, +0), whenever x and y
41   // have different signs and both are not NaNs, we return the number with
42   // positive sign.
43   if (x_bits.sign() != y_bits.sign())
44     return x_bits.is_pos() ? x : y;
45   return x > y ? x : y;
46 }
47 
48 #ifdef LIBC_TYPES_HAS_FLOAT16
49 #if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)
max(float16 x,float16 y)50 template <> LIBC_INLINE float16 max(float16 x, float16 y) {
51   return __builtin_fmaxf16(x, y);
52 }
53 #elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)
max(float16 x,float16 y)54 template <> LIBC_INLINE float16 max(float16 x, float16 y) {
55   FPBits<float16> x_bits(x);
56   FPBits<float16> y_bits(y);
57 
58   int16_t xi = static_cast<int16_t>(x_bits.uintval());
59   int16_t yi = static_cast<int16_t>(y_bits.uintval());
60   return ((xi > yi) != (xi < 0 && yi < 0)) ? x : y;
61 }
62 #endif
63 #endif // LIBC_TYPES_HAS_FLOAT16
64 
65 #if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)
max(float x,float y)66 template <> LIBC_INLINE float max(float x, float y) {
67   return __builtin_fmaxf(x, y);
68 }
69 
max(double x,double y)70 template <> LIBC_INLINE double max(double x, double y) {
71   return __builtin_fmax(x, y);
72 }
73 #endif
74 
75 template <typename T>
min(T x,T y)76 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> min(T x, T y) {
77   FPBits<T> x_bits(x);
78   FPBits<T> y_bits(y);
79 
80   // To make sure that fmin(+0, -0) == -0 == fmin(-0, +0), whenever x and y have
81   // different signs and both are not NaNs, we return the number with negative
82   // sign.
83   if (x_bits.sign() != y_bits.sign())
84     return x_bits.is_neg() ? x : y;
85   return x < y ? x : y;
86 }
87 
88 #ifdef LIBC_TYPES_HAS_FLOAT16
89 #if defined(__LIBC_USE_BUILTIN_FMAXF16_FMINF16)
min(float16 x,float16 y)90 template <> LIBC_INLINE float16 min(float16 x, float16 y) {
91   return __builtin_fminf16(x, y);
92 }
93 #elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)
min(float16 x,float16 y)94 template <> LIBC_INLINE float16 min(float16 x, float16 y) {
95   FPBits<float16> x_bits(x);
96   FPBits<float16> y_bits(y);
97 
98   int16_t xi = static_cast<int16_t>(x_bits.uintval());
99   int16_t yi = static_cast<int16_t>(y_bits.uintval());
100   return ((xi < yi) != (xi < 0 && yi < 0)) ? x : y;
101 }
102 #endif
103 #endif // LIBC_TYPES_HAS_FLOAT16
104 
105 #if defined(__LIBC_USE_BUILTIN_FMAX_FMIN) && !defined(LIBC_TARGET_ARCH_IS_X86)
min(float x,float y)106 template <> LIBC_INLINE float min(float x, float y) {
107   return __builtin_fminf(x, y);
108 }
109 
min(double x,double y)110 template <> LIBC_INLINE double min(double x, double y) {
111   return __builtin_fmin(x, y);
112 }
113 #endif
114 
115 } // namespace internal
116 
117 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmin(T x,T y)118 LIBC_INLINE T fmin(T x, T y) {
119   const FPBits<T> bitx(x), bity(y);
120 
121   if (bitx.is_nan())
122     return y;
123   if (bity.is_nan())
124     return x;
125   return internal::min(x, y);
126 }
127 
128 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmax(T x,T y)129 LIBC_INLINE T fmax(T x, T y) {
130   FPBits<T> bitx(x), bity(y);
131 
132   if (bitx.is_nan())
133     return y;
134   if (bity.is_nan())
135     return x;
136   return internal::max(x, y);
137 }
138 
139 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmaximum(T x,T y)140 LIBC_INLINE T fmaximum(T x, T y) {
141   FPBits<T> bitx(x), bity(y);
142 
143   if (bitx.is_nan())
144     return x;
145   if (bity.is_nan())
146     return y;
147   return internal::max(x, y);
148 }
149 
150 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fminimum(T x,T y)151 LIBC_INLINE T fminimum(T x, T y) {
152   const FPBits<T> bitx(x), bity(y);
153 
154   if (bitx.is_nan())
155     return x;
156   if (bity.is_nan())
157     return y;
158   return internal::min(x, y);
159 }
160 
161 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmaximum_num(T x,T y)162 LIBC_INLINE T fmaximum_num(T x, T y) {
163   FPBits<T> bitx(x), bity(y);
164   if (bitx.is_signaling_nan() || bity.is_signaling_nan()) {
165     fputil::raise_except_if_required(FE_INVALID);
166     if (bitx.is_nan() && bity.is_nan())
167       return FPBits<T>::quiet_nan().get_val();
168   }
169   if (bitx.is_nan())
170     return y;
171   if (bity.is_nan())
172     return x;
173   return internal::max(x, y);
174 }
175 
176 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fminimum_num(T x,T y)177 LIBC_INLINE T fminimum_num(T x, T y) {
178   FPBits<T> bitx(x), bity(y);
179   if (bitx.is_signaling_nan() || bity.is_signaling_nan()) {
180     fputil::raise_except_if_required(FE_INVALID);
181     if (bitx.is_nan() && bity.is_nan())
182       return FPBits<T>::quiet_nan().get_val();
183   }
184   if (bitx.is_nan())
185     return y;
186   if (bity.is_nan())
187     return x;
188   return internal::min(x, y);
189 }
190 
191 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmaximum_mag(T x,T y)192 LIBC_INLINE T fmaximum_mag(T x, T y) {
193   FPBits<T> bitx(x), bity(y);
194 
195   if (abs(x) > abs(y))
196     return x;
197   if (abs(y) > abs(x))
198     return y;
199   return fmaximum(x, y);
200 }
201 
202 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fminimum_mag(T x,T y)203 LIBC_INLINE T fminimum_mag(T x, T y) {
204   FPBits<T> bitx(x), bity(y);
205 
206   if (abs(x) < abs(y))
207     return x;
208   if (abs(y) < abs(x))
209     return y;
210   return fminimum(x, y);
211 }
212 
213 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fmaximum_mag_num(T x,T y)214 LIBC_INLINE T fmaximum_mag_num(T x, T y) {
215   FPBits<T> bitx(x), bity(y);
216 
217   if (abs(x) > abs(y))
218     return x;
219   if (abs(y) > abs(x))
220     return y;
221   return fmaximum_num(x, y);
222 }
223 
224 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fminimum_mag_num(T x,T y)225 LIBC_INLINE T fminimum_mag_num(T x, T y) {
226   FPBits<T> bitx(x), bity(y);
227 
228   if (abs(x) < abs(y))
229     return x;
230   if (abs(y) < abs(x))
231     return y;
232   return fminimum_num(x, y);
233 }
234 
235 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
fdim(T x,T y)236 LIBC_INLINE T fdim(T x, T y) {
237   FPBits<T> bitx(x), bity(y);
238 
239   if (bitx.is_nan()) {
240     return x;
241   }
242 
243   if (bity.is_nan()) {
244     return y;
245   }
246 
247   return (x > y ? x - y : 0);
248 }
249 
250 // Avoid reusing `issignaling` macro.
251 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
issignaling_impl(const T & x)252 LIBC_INLINE int issignaling_impl(const T &x) {
253   FPBits<T> sx(x);
254   return sx.is_signaling_nan();
255 }
256 
257 template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
canonicalize(T & cx,const T & x)258 LIBC_INLINE int canonicalize(T &cx, const T &x) {
259   FPBits<T> sx(x);
260   if constexpr (get_fp_type<T>() == FPType::X86_Binary80) {
261     // All the pseudo and unnormal numbers are not canonical.
262     // More precisely :
263     // Exponent   |       Significand      | Meaning
264     //            | Bits 63-62 | Bits 61-0 |
265     // All Ones   |     00     |    Zero   | Pseudo Infinity, Value = SNaN
266     // All Ones   |     00     |  Non-Zero | Pseudo NaN, Value = SNaN
267     // All Ones   |     01     | Anything  | Pseudo NaN, Value = SNaN
268     //            |   Bit 63   | Bits 62-0 |
269     // All zeroes |   One      | Anything  | Pseudo Denormal, Value =
270     //            |            |           | (−1)**s × m × 2**−16382
271     // All Other  |   Zero     | Anything  | Unnormal, Value = SNaN
272     //  Values    |            |           |
273     bool bit63 = sx.get_implicit_bit();
274     UInt128 mantissa = sx.get_explicit_mantissa();
275     bool bit62 = static_cast<bool>((mantissa & (1ULL << 62)) >> 62);
276     int exponent = sx.get_biased_exponent();
277     if (exponent == 0x7FFF) {
278       if (!bit63 && !bit62) {
279         if (mantissa == 0) {
280           cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();
281           raise_except_if_required(FE_INVALID);
282           return 1;
283         }
284         cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();
285         raise_except_if_required(FE_INVALID);
286         return 1;
287       } else if (!bit63 && bit62) {
288         cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();
289         raise_except_if_required(FE_INVALID);
290         return 1;
291       } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {
292         cx = FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa())
293                  .get_val();
294         raise_except_if_required(FE_INVALID);
295         return 1;
296       } else
297         cx = x;
298     } else if (exponent == 0 && bit63)
299       cx = FPBits<T>::make_value(mantissa, 0).get_val();
300     else if (exponent != 0 && !bit63) {
301       cx = FPBits<T>::quiet_nan(sx.sign(), mantissa).get_val();
302       raise_except_if_required(FE_INVALID);
303       return 1;
304     } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {
305       cx =
306           FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val();
307       raise_except_if_required(FE_INVALID);
308       return 1;
309     } else
310       cx = x;
311   } else if (LIBC_UNLIKELY(sx.is_signaling_nan())) {
312     cx = FPBits<T>::quiet_nan(sx.sign(), sx.get_explicit_mantissa()).get_val();
313     raise_except_if_required(FE_INVALID);
314     return 1;
315   } else
316     cx = x;
317   return 0;
318 }
319 
320 template <typename T>
321 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
totalorder(T x,T y)322 totalorder(T x, T y) {
323   using FPBits = FPBits<T>;
324   FPBits x_bits(x);
325   FPBits y_bits(y);
326 
327   using StorageType = typename FPBits::StorageType;
328   StorageType x_u = x_bits.uintval();
329   StorageType y_u = y_bits.uintval();
330 
331   bool has_neg = ((x_u | y_u) & FPBits::SIGN_MASK) != 0;
332   return x_u == y_u || ((x_u < y_u) != has_neg);
333 }
334 
335 template <typename T>
336 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
totalordermag(T x,T y)337 totalordermag(T x, T y) {
338   return FPBits<T>(x).abs().uintval() <= FPBits<T>(y).abs().uintval();
339 }
340 
341 template <typename T>
getpayload(T x)342 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, T> getpayload(T x) {
343   using FPBits = FPBits<T>;
344   using StorageType = typename FPBits::StorageType;
345   FPBits x_bits(x);
346 
347   if (!x_bits.is_nan())
348     return T(-1.0);
349 
350   StorageType payload = x_bits.uintval() & (FPBits::FRACTION_MASK >> 1);
351 
352   if constexpr (is_big_int_v<StorageType>) {
353     DyadicFloat<FPBits::STORAGE_LEN> payload_dfloat(Sign::POS, 0, payload);
354 
355     return static_cast<T>(payload_dfloat);
356   } else {
357     return static_cast<T>(payload);
358   }
359 }
360 
361 template <bool IsSignaling, typename T>
362 LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
setpayload(T & res,T pl)363 setpayload(T &res, T pl) {
364   using FPBits = FPBits<T>;
365   FPBits pl_bits(pl);
366 
367   // Signaling NaNs don't have the mantissa's MSB set to 1, so they need a
368   // non-zero payload to distinguish them from infinities.
369   if (!IsSignaling && pl_bits.is_zero()) {
370     res = FPBits::quiet_nan(Sign::POS).get_val();
371     return false;
372   }
373 
374   int pl_exp = pl_bits.get_exponent();
375 
376   if (pl_bits.is_neg() || pl_exp < 0 || pl_exp >= FPBits::FRACTION_LEN - 1 ||
377       ((pl_bits.get_mantissa() << pl_exp) & FPBits::FRACTION_MASK) != 0) {
378     res = T(0.0);
379     return true;
380   }
381 
382   using StorageType = typename FPBits::StorageType;
383   StorageType v(pl_bits.get_explicit_mantissa() >>
384                 (FPBits::FRACTION_LEN - pl_exp));
385 
386   if constexpr (IsSignaling)
387     res = FPBits::signaling_nan(Sign::POS, v).get_val();
388   else
389     res = FPBits::quiet_nan(Sign::POS, v).get_val();
390   return false;
391 }
392 
393 } // namespace fputil
394 } // namespace LIBC_NAMESPACE_DECL
395 
396 #endif // LLVM_LIBC_SRC___SUPPORT_FPUTIL_BASICOPERATIONS_H
397