Warning: This file is not a C or C++ file. It does not have highlighting.

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 T abs(T x) {
30 return FPBits<T>(x).abs().get_val();
31}
32
33namespace internal {
34
35template <typename T>
36LIBC_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)
50template <> LIBC_INLINE float16 max(float16 x, float16 y) {
51 return __builtin_fmaxf16(x, y);
52}
53#elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)
54template <> 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)
66template <> LIBC_INLINE float max(float x, float y) {
67 return __builtin_fmaxf(x, y);
68}
69
70template <> LIBC_INLINE double max(double x, double y) {
71 return __builtin_fmax(x, y);
72}
73#endif
74
75template <typename T>
76LIBC_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)
90template <> LIBC_INLINE float16 min(float16 x, float16 y) {
91 return __builtin_fminf16(x, y);
92}
93#elif !defined(LIBC_TARGET_ARCH_IS_AARCH64)
94template <> 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)
106template <> LIBC_INLINE float min(float x, float y) {
107 return __builtin_fminf(x, y);
108}
109
110template <> LIBC_INLINE double min(double x, double y) {
111 return __builtin_fmin(x, y);
112}
113#endif
114
115} // namespace internal
116
117template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
118LIBC_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
128template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
129LIBC_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
139template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
140LIBC_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
150template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
151LIBC_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
161template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
162LIBC_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
176template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
177LIBC_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
191template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
192LIBC_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
202template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
203LIBC_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
213template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
214LIBC_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
224template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
225LIBC_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
235template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
236LIBC_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.
251template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
252LIBC_INLINE int issignaling_impl(const T &x) {
253 FPBits<T> sx(x);
254 return sx.is_signaling_nan();
255}
256
257template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
258LIBC_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
320template <typename T>
321LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
322totalorder(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
335template <typename T>
336LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
337totalordermag(T x, T y) {
338 return FPBits<T>(x).abs().uintval() <= FPBits<T>(y).abs().uintval();
339}
340
341template <typename T>
342LIBC_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
361template <bool IsSignaling, typename T>
362LIBC_INLINE cpp::enable_if_t<cpp::is_floating_point_v<T>, bool>
363setpayload(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

Warning: This file is not a C or C++ file. It does not have highlighting.

source code of libc/src/__support/FPUtil/BasicOperations.h