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