Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/libc/src/__support/FPUtil/BasicOperations.h
213799 views
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>
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>
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)
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)
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)
66
template <> LIBC_INLINE float max(float x, float y) {
67
return __builtin_fmaxf(x, y);
68
}
69
70
template <> LIBC_INLINE double max(double x, double y) {
71
return __builtin_fmax(x, y);
72
}
73
#endif
74
75
template <typename T>
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)
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)
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)
106
template <> LIBC_INLINE float min(float x, float y) {
107
return __builtin_fminf(x, y);
108
}
109
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
337
totalordermag(T x, T y) {
338
return FPBits<T>(x).abs().uintval() <= FPBits<T>(y).abs().uintval();
339
}
340
341
template <typename T>
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>
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
398