@@ -33,12 +33,6 @@ enum class FPType {
33
33
34
34
namespace internal {
35
35
36
- // The type of encoding for supported floating point types.
37
- enum class FPEncoding {
38
- IEEE754,
39
- X86_ExtendedPrecision,
40
- };
41
-
42
36
// Defines the layout (sign, exponent, significand) of a floating point type in
43
37
// memory. It also defines its associated StorageType, i.e., the unsigned
44
38
// integer type used to manipulate its representation.
@@ -49,47 +43,41 @@ template <> struct FPLayout<FPType::IEEE754_Binary16> {
49
43
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1 ;
50
44
LIBC_INLINE_VAR static constexpr int EXP_LEN = 5 ;
51
45
LIBC_INLINE_VAR static constexpr int SIG_LEN = 10 ;
52
- LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
53
46
};
54
47
55
48
template <> struct FPLayout <FPType::IEEE754_Binary32> {
56
49
using StorageType = uint32_t ;
57
50
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1 ;
58
51
LIBC_INLINE_VAR static constexpr int EXP_LEN = 8 ;
59
52
LIBC_INLINE_VAR static constexpr int SIG_LEN = 23 ;
60
- LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
61
53
};
62
54
63
55
template <> struct FPLayout <FPType::IEEE754_Binary64> {
64
56
using StorageType = uint64_t ;
65
57
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1 ;
66
58
LIBC_INLINE_VAR static constexpr int EXP_LEN = 11 ;
67
59
LIBC_INLINE_VAR static constexpr int SIG_LEN = 52 ;
68
- LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
69
60
};
70
61
71
62
template <> struct FPLayout <FPType::IEEE754_Binary128> {
72
63
using StorageType = UInt128;
73
64
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1 ;
74
65
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15 ;
75
66
LIBC_INLINE_VAR static constexpr int SIG_LEN = 112 ;
76
- LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
77
67
};
78
68
79
69
template <> struct FPLayout <FPType::X86_Binary80> {
80
70
using StorageType = UInt128;
81
71
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1 ;
82
72
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15 ;
83
73
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64 ;
84
- LIBC_INLINE_VAR static constexpr auto ENCODING =
85
- FPEncoding::X86_ExtendedPrecision;
86
74
};
87
75
88
76
} // namespace internal
89
77
90
- // FPBaseMasksAndShifts derives useful constants from the FPLayout.
78
+ // FPRepBase derives useful constants from the FPLayout.
91
79
template <FPType fp_type>
92
- struct FPBaseMasksAndShifts : public internal ::FPLayout<fp_type> {
80
+ struct FPRepBase : public internal ::FPLayout<fp_type> {
93
81
private:
94
82
using UP = internal::FPLayout<fp_type>;
95
83
@@ -149,95 +137,67 @@ struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
149
137
return StorageType (1 ) << position;
150
138
}
151
139
152
- public:
140
+ // Merge bits from 'a' and 'b' values according to 'mask'.
141
+ // Use 'a' bits when corresponding 'mask' bits are zeroes and 'b' bits when
142
+ // corresponding bits are ones.
143
+ LIBC_INLINE static constexpr StorageType merge (StorageType a, StorageType b,
144
+ StorageType mask) {
145
+ // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge
146
+ return a ^ ((a ^ b) & mask);
147
+ }
148
+
149
+ protected:
153
150
// The number of bits after the decimal dot when the number is in normal form.
154
151
LIBC_INLINE_VAR static constexpr int FRACTION_LEN =
155
- UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision ? SIG_LEN - 1
156
- : SIG_LEN;
152
+ fp_type == FPType::X86_Binary80 ? SIG_LEN - 1 : SIG_LEN;
157
153
LIBC_INLINE_VAR static constexpr uint32_t MANTISSA_PRECISION =
158
154
FRACTION_LEN + 1 ;
159
155
LIBC_INLINE_VAR static constexpr StorageType FRACTION_MASK =
160
156
mask_trailing_ones<StorageType, FRACTION_LEN>();
161
157
162
- protected:
163
158
// If a number x is a NAN, then it is a quiet NAN if:
164
159
// QUIET_NAN_MASK & bits(x) != 0
165
160
LIBC_INLINE_VAR static constexpr StorageType QUIET_NAN_MASK =
166
- UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision
161
+ fp_type == FPType::X86_Binary80
167
162
? bit_at(SIG_LEN - 1 ) | bit_at(SIG_LEN - 2 ) // 0b1100...
168
163
: bit_at(SIG_LEN - 1 ); // 0b1000...
169
164
170
165
// If a number x is a NAN, then it is a signalling NAN if:
171
166
// SIGNALING_NAN_MASK & bits(x) != 0
172
167
LIBC_INLINE_VAR static constexpr StorageType SIGNALING_NAN_MASK =
173
- UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision
168
+ fp_type == FPType::X86_Binary80
174
169
? bit_at(SIG_LEN - 1 ) | bit_at(SIG_LEN - 3 ) // 0b1010...
175
170
: bit_at(SIG_LEN - 2 ); // 0b0100...
176
- };
177
-
178
- namespace internal {
179
171
180
- // This is a temporary class to unify common methods and properties between
181
- // FPBits and FPBits<long double>.
182
- template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts <fp_type> {
183
- using UP = FPBaseMasksAndShifts<fp_type>;
184
- using typename UP::StorageType;
185
- using UP::TOTAL_LEN;
186
-
187
- protected:
188
- using UP::EXP_SIG_MASK;
189
- using UP::QUIET_NAN_MASK;
172
+ // The floating point number representation as an unsigned integer.
173
+ StorageType bits = 0 ;
190
174
191
175
public:
192
- using UP::EXP_BIAS;
193
- using UP::EXP_LEN;
194
- using UP::EXP_MASK;
195
- using UP::EXP_MASK_SHIFT;
196
- using UP::FP_MASK;
197
- using UP::FRACTION_LEN;
198
- using UP::FRACTION_MASK;
199
- using UP::MANTISSA_PRECISION;
200
- using UP::SIGN_MASK;
201
- using UP::STORAGE_LEN;
202
-
203
- // Reinterpreting bits as an integer value and interpreting the bits of an
204
- // integer value as a floating point value is used in tests. So, a convenient
205
- // type is provided for such reinterpretations.
206
- StorageType bits;
207
-
208
- LIBC_INLINE constexpr FPRep () : bits(0 ) {}
209
- LIBC_INLINE explicit constexpr FPRep (StorageType bits) : bits(bits) {}
210
-
211
- LIBC_INLINE constexpr void set_mantissa (StorageType mantVal) {
212
- mantVal &= FRACTION_MASK;
213
- bits &= ~FRACTION_MASK;
214
- bits |= mantVal;
215
- }
216
-
217
- LIBC_INLINE constexpr StorageType get_mantissa () const {
218
- return bits & FRACTION_MASK;
176
+ LIBC_INLINE constexpr bool get_sign () const {
177
+ return (bits & SIGN_MASK) != 0 ;
219
178
}
220
179
221
180
LIBC_INLINE constexpr void set_sign (bool signVal) {
222
181
if (get_sign () != signVal)
223
182
bits ^= SIGN_MASK;
224
183
}
225
184
226
- LIBC_INLINE constexpr bool get_sign () const {
227
- return ( bits & SIGN_MASK) != 0 ;
185
+ LIBC_INLINE constexpr StorageType get_mantissa () const {
186
+ return bits & FRACTION_MASK ;
228
187
}
229
188
230
- LIBC_INLINE constexpr void set_biased_exponent (StorageType biased) {
231
- // clear exponent bits
232
- bits &= ~EXP_MASK;
233
- // set exponent bits
234
- bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
189
+ LIBC_INLINE constexpr void set_mantissa (StorageType mantVal) {
190
+ bits = merge (bits, mantVal, FRACTION_MASK);
235
191
}
236
192
237
193
LIBC_INLINE constexpr uint16_t get_biased_exponent () const {
238
194
return uint16_t ((bits & EXP_MASK) >> EXP_MASK_SHIFT);
239
195
}
240
196
197
+ LIBC_INLINE constexpr void set_biased_exponent (StorageType biased) {
198
+ bits = merge (bits, biased << EXP_MASK_SHIFT, EXP_MASK);
199
+ }
200
+
241
201
LIBC_INLINE constexpr int get_exponent () const {
242
202
return int (get_biased_exponent ()) - EXP_BIAS;
243
203
}
@@ -266,6 +226,23 @@ template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
266
226
}
267
227
};
268
228
229
+ namespace internal {
230
+
231
+ // Manipulates the representation of a floating point number defined by its
232
+ // FPType. This layer is architecture agnostic and does not handle C++ floating
233
+ // point types directly ('float', 'double' and 'long double'). Use the FPBits
234
+ // below if needed.
235
+ //
236
+ // TODO: Specialize this class for FPType::X86_Binary80 and remove ad-hoc logic
237
+ // from FPRepBase.
238
+ template <FPType fp_type> struct FPRep : public FPRepBase <fp_type> {
239
+ using UP = FPRepBase<fp_type>;
240
+ using typename UP::StorageType;
241
+ using UP::FRACTION_LEN;
242
+ using UP::FRACTION_MASK;
243
+ using UP::MANTISSA_PRECISION;
244
+ };
245
+
269
246
} // namespace internal
270
247
271
248
// Returns the FPType corresponding to C++ type T on the host.
@@ -311,14 +288,16 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
311
288
static_assert (cpp::is_floating_point_v<T>,
312
289
" FPBits instantiated with invalid type." );
313
290
using UP = internal::FPRep<get_fp_type<T>()>;
314
- using StorageType = typename UP::StorageType;
315
- using UP::bits;
316
291
317
292
private:
318
293
using UP::EXP_SIG_MASK;
319
294
using UP::QUIET_NAN_MASK;
295
+ using UP::SIG_LEN;
296
+ using UP::SIG_MASK;
320
297
321
298
public:
299
+ using StorageType = typename UP::StorageType;
300
+ using UP::bits;
322
301
using UP::EXP_BIAS;
323
302
using UP::EXP_LEN;
324
303
using UP::EXP_MASK;
@@ -327,46 +306,47 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
327
306
using UP::FRACTION_MASK;
328
307
using UP::SIGN_MASK;
329
308
using UP::TOTAL_LEN;
309
+ using UP::UP;
330
310
331
311
using UP::get_biased_exponent;
332
312
using UP::is_zero;
333
-
334
- // The function return mantissa with the implicit bit set iff the current
335
- // value is a valid normal number.
336
- LIBC_INLINE constexpr StorageType get_explicit_mantissa () {
337
- return ((get_biased_exponent () > 0 && !is_inf_or_nan ())
338
- ? (FRACTION_MASK + 1 )
339
- : 0 ) |
340
- (FRACTION_MASK & bits);
341
- }
342
-
313
+ // Constants.
343
314
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1 ;
344
315
static constexpr StorageType MIN_SUBNORMAL = StorageType(1 );
345
316
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
346
317
static constexpr StorageType MIN_NORMAL = (StorageType(1 ) << FRACTION_LEN);
347
318
static constexpr StorageType MAX_NORMAL =
348
- ((StorageType(MAX_BIASED_EXPONENT) - 1 ) << FRACTION_LEN) | MAX_SUBNORMAL;
349
-
350
- // We don't want accidental type promotions/conversions, so we require exact
351
- // type match.
352
- template <typename XType, cpp::enable_if_t <cpp::is_same_v<T, XType>, int > = 0 >
353
- LIBC_INLINE constexpr explicit FPBits (XType x)
354
- : UP(cpp::bit_cast<StorageType>(x)) {}
319
+ (StorageType(MAX_BIASED_EXPONENT - 1 ) << SIG_LEN) | SIG_MASK;
355
320
356
- template <typename XType,
357
- cpp::enable_if_t <cpp::is_same_v<XType, StorageType>, int > = 0 >
358
- LIBC_INLINE constexpr explicit FPBits (XType x) : UP(x) {}
321
+ // Constructors.
322
+ LIBC_INLINE constexpr FPBits () = default;
359
323
360
- LIBC_INLINE constexpr FPBits () : UP() {}
361
-
362
- LIBC_INLINE constexpr void set_val (T value) {
363
- bits = cpp::bit_cast<StorageType>(value);
324
+ template <typename XType> LIBC_INLINE constexpr explicit FPBits (XType x) {
325
+ using Unqual = typename cpp::remove_cv_t <XType>;
326
+ if constexpr (cpp::is_same_v<Unqual, T>) {
327
+ bits = cpp::bit_cast<StorageType>(x);
328
+ } else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
329
+ bits = x;
330
+ } else {
331
+ // We don't want accidental type promotions/conversions, so we require
332
+ // exact type match.
333
+ static_assert (cpp::always_false<XType>);
334
+ }
364
335
}
365
-
336
+ // Floating-point conversions.
366
337
LIBC_INLINE constexpr T get_val () const { return cpp::bit_cast<T>(bits); }
367
338
368
339
LIBC_INLINE constexpr explicit operator T () const { return get_val (); }
369
340
341
+ // The function return mantissa with the implicit bit set iff the current
342
+ // value is a valid normal number.
343
+ LIBC_INLINE constexpr StorageType get_explicit_mantissa () {
344
+ return ((get_biased_exponent () > 0 && !is_inf_or_nan ())
345
+ ? (FRACTION_MASK + 1 )
346
+ : 0 ) |
347
+ (FRACTION_MASK & bits);
348
+ }
349
+
370
350
LIBC_INLINE constexpr bool is_inf () const {
371
351
return (bits & EXP_SIG_MASK) == EXP_MASK;
372
352
}
@@ -387,14 +367,22 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
387
367
return FPBits (bits & EXP_SIG_MASK);
388
368
}
389
369
370
+ // Methods below this are used by tests.
371
+
390
372
LIBC_INLINE static constexpr T zero (bool sign = false ) {
391
- return FPBits (sign ? SIGN_MASK : StorageType (0 )).get_val ();
373
+ StorageType rep = (sign ? SIGN_MASK : StorageType (0 )) // sign
374
+ | 0 // exponent
375
+ | 0 ; // mantissa
376
+ return FPBits (rep).get_val ();
392
377
}
393
378
394
379
LIBC_INLINE static constexpr T neg_zero () { return zero (true ); }
395
380
396
381
LIBC_INLINE static constexpr T inf (bool sign = false ) {
397
- return FPBits ((sign ? SIGN_MASK : StorageType (0 )) | EXP_MASK).get_val ();
382
+ StorageType rep = (sign ? SIGN_MASK : StorageType (0 )) // sign
383
+ | EXP_MASK // exponent
384
+ | 0 ; // mantissa
385
+ return FPBits (rep).get_val ();
398
386
}
399
387
400
388
LIBC_INLINE static constexpr T neg_inf () { return inf (true ); }
@@ -416,15 +404,24 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
416
404
}
417
405
418
406
LIBC_INLINE static constexpr T build_nan (StorageType v) {
419
- FPBits<T> bits (inf ());
420
- bits.set_mantissa (v);
421
- return T (bits);
407
+ StorageType rep = 0 // sign
408
+ | EXP_MASK // exponent
409
+ | (v & FRACTION_MASK); // mantissa
410
+ return FPBits (rep).get_val ();
422
411
}
423
412
424
413
LIBC_INLINE static constexpr T build_quiet_nan (StorageType v) {
425
414
return build_nan (QUIET_NAN_MASK | v);
426
415
}
427
416
417
+ LIBC_INLINE static constexpr FPBits<T>
418
+ create_value (bool sign, StorageType biased_exp, StorageType mantissa) {
419
+ StorageType rep = (sign ? SIGN_MASK : StorageType (0 )) // sign
420
+ | ((biased_exp << EXP_MASK_SHIFT) & EXP_MASK) // exponent
421
+ | (mantissa & FRACTION_MASK); // mantissa
422
+ return FPBits (rep);
423
+ }
424
+
428
425
// The function convert integer number and unbiased exponent to proper float
429
426
// T type:
430
427
// Result = number * 2^(ep+1 - exponent_bias)
@@ -452,15 +449,6 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
452
449
}
453
450
return result;
454
451
}
455
-
456
- LIBC_INLINE static constexpr FPBits<T>
457
- create_value (bool sign, StorageType biased_exp, StorageType mantissa) {
458
- FPBits<T> result;
459
- result.set_sign (sign);
460
- result.set_biased_exponent (biased_exp);
461
- result.set_mantissa (mantissa);
462
- return result;
463
- }
464
452
};
465
453
466
454
} // namespace fputil
0 commit comments