Skip to content

Commit 36b6f77

Browse files
authored
[libc][NFC] Unify FPBits implementations (llvm#76033)
`FPBits` is currently implemented as a general case and a specialization for `long double` when `long double` is x86 80-bit extended precision. This patch is a first of a series to provide an implementation based on `FPType` instead of the C++ float type (which implementation is architecture dependent).
1 parent a047675 commit 36b6f77

File tree

3 files changed

+124
-152
lines changed

3 files changed

+124
-152
lines changed

libc/src/__support/FPUtil/FPBits.h

Lines changed: 103 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -21,41 +21,37 @@
2121
namespace LIBC_NAMESPACE {
2222
namespace fputil {
2323

24-
// A generic class to represent single precision, double precision, and quad
25-
// precision IEEE 754 floating point formats.
26-
// On most platforms, the 'float' type corresponds to single precision floating
27-
// point numbers, the 'double' type corresponds to double precision floating
28-
// point numers, and the 'long double' type corresponds to the quad precision
29-
// floating numbers. On x86 platforms however, the 'long double' type maps to
30-
// an x87 floating point format. This format is an IEEE 754 extension format.
31-
// It is handled as an explicit specialization of this class.
32-
template <typename T> struct FPBits : private FloatProperties<T> {
33-
static_assert(cpp::is_floating_point_v<T>,
34-
"FPBits instantiated with invalid type.");
35-
using typename FloatProperties<T>::StorageType;
36-
using FloatProperties<T>::TOTAL_LEN;
24+
namespace internal {
3725

38-
private:
39-
using FloatProperties<T>::EXP_SIG_MASK;
40-
41-
public:
42-
using FloatProperties<T>::EXP_MASK;
43-
using FloatProperties<T>::EXP_BIAS;
44-
using FloatProperties<T>::EXP_LEN;
45-
using FloatProperties<T>::FRACTION_MASK;
46-
using FloatProperties<T>::FRACTION_LEN;
26+
// This is a temporary class to unify common methods and properties between
27+
// FPBits and FPBits<long double>.
28+
template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
29+
using UP = FPProperties<fp_type>;
30+
using typename UP::StorageType;
31+
using UP::TOTAL_LEN;
4732

48-
private:
49-
using FloatProperties<T>::QUIET_NAN_MASK;
33+
protected:
34+
using UP::EXP_SIG_MASK;
35+
using UP::QUIET_NAN_MASK;
5036

5137
public:
52-
using FloatProperties<T>::SIGN_MASK;
38+
using UP::EXP_BIAS;
39+
using UP::EXP_LEN;
40+
using UP::EXP_MASK;
41+
using UP::EXP_MASK_SHIFT;
42+
using UP::FP_MASK;
43+
using UP::FRACTION_LEN;
44+
using UP::FRACTION_MASK;
45+
using UP::SIGN_MASK;
5346

5447
// Reinterpreting bits as an integer value and interpreting the bits of an
5548
// integer value as a floating point value is used in tests. So, a convenient
5649
// type is provided for such reinterpretations.
5750
StorageType bits;
5851

52+
LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
53+
LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
54+
5955
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
6056
mantVal &= FRACTION_MASK;
6157
bits &= ~FRACTION_MASK;
@@ -66,16 +62,89 @@ template <typename T> struct FPBits : private FloatProperties<T> {
6662
return bits & FRACTION_MASK;
6763
}
6864

69-
LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
70-
expVal = (expVal << FRACTION_LEN) & EXP_MASK;
65+
LIBC_INLINE constexpr void set_sign(bool signVal) {
66+
if (get_sign() != signVal)
67+
bits ^= SIGN_MASK;
68+
}
69+
70+
LIBC_INLINE constexpr bool get_sign() const {
71+
return (bits & SIGN_MASK) != 0;
72+
}
73+
74+
LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
75+
// clear exponent bits
7176
bits &= ~EXP_MASK;
72-
bits |= expVal;
77+
// set exponent bits
78+
bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
7379
}
7480

7581
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
76-
return uint16_t((bits & EXP_MASK) >> FRACTION_LEN);
82+
return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
83+
}
84+
85+
LIBC_INLINE constexpr int get_exponent() const {
86+
return int(get_biased_exponent()) - EXP_BIAS;
7787
}
7888

89+
// If the number is subnormal, the exponent is treated as if it were the
90+
// minimum exponent for a normal number. This is to keep continuity between
91+
// the normal and subnormal ranges, but it causes problems for functions where
92+
// values are calculated from the exponent, since just subtracting the bias
93+
// will give a slightly incorrect result. Additionally, zero has an exponent
94+
// of zero, and that should actually be treated as zero.
95+
LIBC_INLINE constexpr int get_explicit_exponent() const {
96+
const int biased_exp = int(get_biased_exponent());
97+
if (is_zero()) {
98+
return 0;
99+
} else if (biased_exp == 0) {
100+
return 1 - EXP_BIAS;
101+
} else {
102+
return biased_exp - EXP_BIAS;
103+
}
104+
}
105+
106+
LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
107+
108+
LIBC_INLINE constexpr bool is_zero() const {
109+
return (bits & EXP_SIG_MASK) == 0;
110+
}
111+
};
112+
113+
} // namespace internal
114+
115+
// A generic class to represent single precision, double precision, and quad
116+
// precision IEEE 754 floating point formats.
117+
// On most platforms, the 'float' type corresponds to single precision floating
118+
// point numbers, the 'double' type corresponds to double precision floating
119+
// point numers, and the 'long double' type corresponds to the quad precision
120+
// floating numbers. On x86 platforms however, the 'long double' type maps to
121+
// an x87 floating point format. This format is an IEEE 754 extension format.
122+
// It is handled as an explicit specialization of this class.
123+
template <typename T>
124+
struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
125+
static_assert(cpp::is_floating_point_v<T>,
126+
"FPBits instantiated with invalid type.");
127+
using UP = internal::FPBitsCommon<get_fp_type<T>()>;
128+
using StorageType = typename UP::StorageType;
129+
using UP::bits;
130+
131+
private:
132+
using UP::EXP_SIG_MASK;
133+
using UP::QUIET_NAN_MASK;
134+
135+
public:
136+
using UP::EXP_BIAS;
137+
using UP::EXP_LEN;
138+
using UP::EXP_MASK;
139+
using UP::EXP_MASK_SHIFT;
140+
using UP::FRACTION_LEN;
141+
using UP::FRACTION_MASK;
142+
using UP::SIGN_MASK;
143+
using UP::TOTAL_LEN;
144+
145+
using UP::get_biased_exponent;
146+
using UP::is_zero;
147+
79148
// The function return mantissa with the implicit bit set iff the current
80149
// value is a valid normal number.
81150
LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
@@ -85,19 +154,6 @@ template <typename T> struct FPBits : private FloatProperties<T> {
85154
(FRACTION_MASK & bits);
86155
}
87156

88-
LIBC_INLINE constexpr void set_sign(bool signVal) {
89-
bits |= SIGN_MASK;
90-
if (!signVal)
91-
bits -= SIGN_MASK;
92-
}
93-
94-
LIBC_INLINE constexpr bool get_sign() const {
95-
return (bits & SIGN_MASK) != 0;
96-
}
97-
98-
static_assert(sizeof(T) == sizeof(StorageType),
99-
"Data type and integral representation have different sizes.");
100-
101157
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
102158
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
103159
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
@@ -109,49 +165,21 @@ template <typename T> struct FPBits : private FloatProperties<T> {
109165
// type match.
110166
template <typename XType, cpp::enable_if_t<cpp::is_same_v<T, XType>, int> = 0>
111167
LIBC_INLINE constexpr explicit FPBits(XType x)
112-
: bits(cpp::bit_cast<StorageType>(x)) {}
168+
: UP(cpp::bit_cast<StorageType>(x)) {}
113169

114170
template <typename XType,
115171
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
116-
LIBC_INLINE constexpr explicit FPBits(XType x) : bits(x) {}
117-
118-
LIBC_INLINE constexpr FPBits() : bits(0) {}
172+
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
119173

120-
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
174+
LIBC_INLINE constexpr FPBits() : UP() {}
121175

122176
LIBC_INLINE constexpr void set_val(T value) {
123177
bits = cpp::bit_cast<StorageType>(value);
124178
}
125179

126-
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
127-
128-
LIBC_INLINE constexpr StorageType uintval() const { return bits; }
129-
130-
LIBC_INLINE constexpr int get_exponent() const {
131-
return int(get_biased_exponent()) - EXP_BIAS;
132-
}
133-
134-
// If the number is subnormal, the exponent is treated as if it were the
135-
// minimum exponent for a normal number. This is to keep continuity between
136-
// the normal and subnormal ranges, but it causes problems for functions where
137-
// values are calculated from the exponent, since just subtracting the bias
138-
// will give a slightly incorrect result. Additionally, zero has an exponent
139-
// of zero, and that should actually be treated as zero.
140-
LIBC_INLINE constexpr int get_explicit_exponent() const {
141-
const int biased_exp = int(get_biased_exponent());
142-
if (is_zero()) {
143-
return 0;
144-
} else if (biased_exp == 0) {
145-
return 1 - EXP_BIAS;
146-
} else {
147-
return biased_exp - EXP_BIAS;
148-
}
149-
}
180+
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
150181

151-
LIBC_INLINE constexpr bool is_zero() const {
152-
// Remove sign bit by shift
153-
return (bits << 1) == 0;
154-
}
182+
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
155183

156184
LIBC_INLINE constexpr bool is_inf() const {
157185
return (bits & EXP_SIG_MASK) == EXP_MASK;

libc/src/__support/FPUtil/FloatProperties.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ struct FPProperties : public internal::FPBaseProperties<fp_type> {
111111
(1U << (EXP_LEN - 1U)) - 1U;
112112
static_assert(EXP_BIAS > 0);
113113

114-
private:
114+
protected:
115115
// The shift amount to get the *significand* part to the least significant
116116
// bit. Always `0` but kept for consistency.
117117
LIBC_INLINE_VAR static constexpr int SIG_MASK_SHIFT = 0;

libc/src/__support/FPUtil/x86_64/LongDoubleBits.h

Lines changed: 20 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,27 @@
2626
namespace LIBC_NAMESPACE {
2727
namespace fputil {
2828

29-
template <> struct FPBits<long double> : private FloatProperties<long double> {
30-
using typename FloatProperties<long double>::StorageType;
31-
using FloatProperties<long double>::TOTAL_LEN;
32-
using FloatProperties<long double>::EXP_MASK;
33-
using FloatProperties<long double>::EXP_BIAS;
34-
using FloatProperties<long double>::EXP_LEN;
35-
using FloatProperties<long double>::FRACTION_MASK;
36-
using FloatProperties<long double>::FRACTION_LEN;
29+
template <>
30+
struct FPBits<long double>
31+
: public internal::FPBitsCommon<FPType::X86_Binary80> {
32+
using UP = internal::FPBitsCommon<FPType::X86_Binary80>;
33+
using StorageType = typename UP::StorageType;
34+
using UP::bits;
3735

3836
private:
39-
using FloatProperties<long double>::QUIET_NAN_MASK;
37+
using UP::EXP_SIG_MASK;
38+
using UP::QUIET_NAN_MASK;
4039

4140
public:
42-
using FloatProperties<long double>::SIGN_MASK;
41+
using UP::EXP_BIAS;
42+
using UP::EXP_LEN;
43+
using UP::EXP_MASK;
44+
using UP::EXP_MASK_SHIFT;
45+
using UP::FP_MASK;
46+
using UP::FRACTION_LEN;
47+
using UP::FRACTION_MASK;
48+
using UP::SIGN_MASK;
49+
using UP::TOTAL_LEN;
4350

4451
static constexpr int MAX_BIASED_EXPONENT = 0x7FFF;
4552
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
@@ -51,35 +58,13 @@ template <> struct FPBits<long double> : private FloatProperties<long double> {
5158
(StorageType(MAX_BIASED_EXPONENT - 1) << (FRACTION_LEN + 1)) |
5259
(StorageType(1) << FRACTION_LEN) | MAX_SUBNORMAL;
5360

54-
StorageType bits;
55-
56-
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
57-
mantVal &= FRACTION_MASK;
58-
bits &= ~FRACTION_MASK;
59-
bits |= mantVal;
60-
}
61-
62-
LIBC_INLINE constexpr StorageType get_mantissa() const {
63-
return bits & FRACTION_MASK;
64-
}
65-
6661
LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
6762
// The x86 80 bit float represents the leading digit of the mantissa
6863
// explicitly. This is the mask for that bit.
6964
constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1) << FRACTION_LEN;
7065
return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
7166
}
7267

73-
LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
74-
expVal = (expVal << (TOTAL_LEN - 1 - EXP_LEN)) & EXP_MASK;
75-
bits &= ~EXP_MASK;
76-
bits |= expVal;
77-
}
78-
79-
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
80-
return uint16_t((bits & EXP_MASK) >> (TOTAL_LEN - 1 - EXP_LEN));
81-
}
82-
8368
LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
8469
bits &= ~(StorageType(1) << FRACTION_LEN);
8570
bits |= (StorageType(implicitVal) << FRACTION_LEN);
@@ -89,70 +74,29 @@ template <> struct FPBits<long double> : private FloatProperties<long double> {
8974
return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
9075
}
9176

92-
LIBC_INLINE constexpr void set_sign(bool signVal) {
93-
bits &= ~SIGN_MASK;
94-
StorageType sign1 = StorageType(signVal) << (TOTAL_LEN - 1);
95-
bits |= sign1;
96-
}
97-
98-
LIBC_INLINE constexpr bool get_sign() const {
99-
return bool((bits & SIGN_MASK) >> (TOTAL_LEN - 1));
100-
}
101-
102-
LIBC_INLINE constexpr FPBits() : bits(0) {}
77+
LIBC_INLINE constexpr FPBits() : UP() {}
10378

10479
template <typename XType,
10580
cpp::enable_if_t<cpp::is_same_v<long double, XType>, int> = 0>
10681
LIBC_INLINE constexpr explicit FPBits(XType x)
107-
: bits(cpp::bit_cast<StorageType>(x)) {
82+
: UP(cpp::bit_cast<StorageType>(x)) {
10883
// bits starts uninitialized, and setting it to a long double only
10984
// overwrites the first 80 bits. This clears those upper bits.
11085
bits = bits & ((StorageType(1) << 80) - 1);
11186
}
11287

11388
template <typename XType,
11489
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
115-
LIBC_INLINE constexpr explicit FPBits(XType x) : bits(x) {}
90+
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
11691

11792
LIBC_INLINE constexpr operator long double() {
11893
return cpp::bit_cast<long double>(bits);
11994
}
12095

121-
LIBC_INLINE constexpr StorageType uintval() {
122-
// We zero the padding bits as they can contain garbage.
123-
return bits & FP_MASK;
124-
}
125-
12696
LIBC_INLINE constexpr long double get_val() const {
12797
return cpp::bit_cast<long double>(bits);
12898
}
12999

130-
LIBC_INLINE constexpr int get_exponent() const {
131-
return int(get_biased_exponent()) - EXP_BIAS;
132-
}
133-
134-
// If the number is subnormal, the exponent is treated as if it were the
135-
// minimum exponent for a normal number. This is to keep continuity between
136-
// the normal and subnormal ranges, but it causes problems for functions where
137-
// values are calculated from the exponent, since just subtracting the bias
138-
// will give a slightly incorrect result. Additionally, zero has an exponent
139-
// of zero, and that should actually be treated as zero.
140-
LIBC_INLINE constexpr int get_explicit_exponent() const {
141-
const int biased_exp = int(get_biased_exponent());
142-
if (is_zero()) {
143-
return 0;
144-
} else if (biased_exp == 0) {
145-
return 1 - EXP_BIAS;
146-
} else {
147-
return biased_exp - EXP_BIAS;
148-
}
149-
}
150-
151-
LIBC_INLINE constexpr bool is_zero() const {
152-
return get_biased_exponent() == 0 && get_mantissa() == 0 &&
153-
get_implicit_bit() == 0;
154-
}
155-
156100
LIBC_INLINE constexpr bool is_inf() const {
157101
return get_biased_exponent() == MAX_BIASED_EXPONENT &&
158102
get_mantissa() == 0 && get_implicit_bit() == 1;

0 commit comments

Comments
 (0)