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