Skip to content

Commit e4f3ec2

Browse files
authored
[libc][NFC] Simplify FloatProperties implementation (#74821)
This is a follow up on #74481 that migrates code from all specializations into `FPCommonProperties` (except for `EXPLICIT_BIT_MASK` in the `X86_Binary80` specialization)
1 parent 898db11 commit e4f3ec2

File tree

1 file changed

+85
-83
lines changed

1 file changed

+85
-83
lines changed

libc/src/__support/FPUtil/FloatProperties.h

Lines changed: 85 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -80,27 +80,105 @@ template <> struct FPBaseProperties<FPType::X86_Binary80> {
8080
FPEncoding::X86_ExtendedPrecision;
8181
};
8282

83+
// TODO: Move this utility elsewhere.
84+
template <typename T, size_t count> static constexpr T mask_trailing_ones() {
85+
static_assert(cpp::is_unsigned_v<T>);
86+
constexpr unsigned t_bits = CHAR_BIT * sizeof(T);
87+
static_assert(count <= t_bits && "Invalid bit index");
88+
return count == 0 ? 0 : (T(-1) >> (t_bits - count));
89+
}
90+
8391
// Derives more properties from 'FPBaseProperties' above.
8492
// This class serves as a halfway point between 'FPBaseProperties' and
8593
// 'FPProperties' below.
8694
template <FPType fp_type>
8795
struct FPCommonProperties : private FPBaseProperties<fp_type> {
96+
private:
8897
using UP = FPBaseProperties<fp_type>;
89-
using BitsType = typename UP::UIntType;
98+
using UP::EXP_BITS;
99+
using UP::SIG_BITS;
100+
using UP::TOTAL_BITS;
101+
using UIntType = typename UP::UIntType;
102+
103+
LIBC_INLINE_VAR static constexpr int STORAGE_BITS =
104+
sizeof(UIntType) * CHAR_BIT;
105+
static_assert(STORAGE_BITS >= TOTAL_BITS);
90106

91-
LIBC_INLINE_VAR static constexpr uint32_t BIT_WIDTH = UP::TOTAL_BITS;
92-
LIBC_INLINE_VAR static constexpr uint32_t MANTISSA_WIDTH = UP::SIG_BITS;
93-
LIBC_INLINE_VAR static constexpr uint32_t EXPONENT_WIDTH = UP::EXP_BITS;
107+
// The number of bits to represent sign.
108+
// For documentation purpose, always 1.
109+
LIBC_INLINE_VAR static constexpr int SIGN_BITS = 1;
110+
static_assert(SIGN_BITS + EXP_BITS + SIG_BITS == TOTAL_BITS);
94111

95112
// The exponent bias. Always positive.
113+
LIBC_INLINE_VAR static constexpr int32_t EXP_BIAS =
114+
(1U << (EXP_BITS - 1U)) - 1U;
115+
static_assert(EXP_BIAS > 0);
116+
117+
// Shifts
118+
LIBC_INLINE_VAR static constexpr int SIG_MASK_SHIFT = 0;
119+
LIBC_INLINE_VAR static constexpr int EXP_MASK_SHIFT = SIG_BITS;
120+
LIBC_INLINE_VAR static constexpr int SIGN_MASK_SHIFT = SIG_BITS + EXP_BITS;
121+
122+
// Masks
123+
LIBC_INLINE_VAR static constexpr UIntType SIG_MASK =
124+
mask_trailing_ones<UIntType, SIG_BITS>() << SIG_MASK_SHIFT;
125+
LIBC_INLINE_VAR static constexpr UIntType EXP_MASK =
126+
mask_trailing_ones<UIntType, EXP_BITS>() << EXP_MASK_SHIFT;
127+
// Trailing underscore on SIGN_MASK_ is temporary - it will be removed
128+
// once we can replace the public part below with the private one.
129+
LIBC_INLINE_VAR static constexpr UIntType SIGN_MASK_ =
130+
mask_trailing_ones<UIntType, SIGN_BITS>() << SIGN_MASK_SHIFT;
131+
LIBC_INLINE_VAR static constexpr UIntType FP_MASK =
132+
mask_trailing_ones<UIntType, TOTAL_BITS>();
133+
static_assert((SIG_MASK & EXP_MASK & SIGN_MASK_) == 0, "masks disjoint");
134+
static_assert((SIG_MASK | EXP_MASK | SIGN_MASK_) == FP_MASK, "masks covers");
135+
136+
LIBC_INLINE static constexpr UIntType bit_at(int position) {
137+
return UIntType(1) << position;
138+
}
139+
140+
LIBC_INLINE_VAR static constexpr UIntType QNAN_MASK =
141+
UP::ENCODING == FPEncoding::X86_ExtendedPrecision
142+
? bit_at(SIG_BITS - 1) | bit_at(SIG_BITS - 2) // 0b1100...
143+
: bit_at(SIG_BITS - 1); // 0b1000...
144+
145+
LIBC_INLINE_VAR static constexpr UIntType SNAN_MASK =
146+
UP::ENCODING == FPEncoding::X86_ExtendedPrecision
147+
? bit_at(SIG_BITS - 1) | bit_at(SIG_BITS - 3) // 0b1010...
148+
: bit_at(SIG_BITS - 2); // 0b0100...
149+
150+
// The number of bits after the decimal dot when the number if in normal form.
151+
LIBC_INLINE_VAR static constexpr int FRACTION_BITS =
152+
UP::ENCODING == FPEncoding::X86_ExtendedPrecision ? SIG_BITS - 1
153+
: SIG_BITS;
154+
155+
public:
156+
// Public facing API to keep the change local to this file.
157+
using BitsType = UIntType;
158+
159+
LIBC_INLINE_VAR static constexpr uint32_t BIT_WIDTH = TOTAL_BITS;
160+
LIBC_INLINE_VAR static constexpr uint32_t MANTISSA_WIDTH = FRACTION_BITS;
161+
LIBC_INLINE_VAR static constexpr uint32_t MANTISSA_PRECISION =
162+
MANTISSA_WIDTH + 1;
163+
LIBC_INLINE_VAR static constexpr BitsType MANTISSA_MASK =
164+
mask_trailing_ones<UIntType, MANTISSA_WIDTH>();
165+
LIBC_INLINE_VAR static constexpr uint32_t EXPONENT_WIDTH = EXP_BITS;
96166
LIBC_INLINE_VAR static constexpr uint32_t EXPONENT_BIAS =
97-
(1U << (UP::EXP_BITS - 1U)) - 1U;
98-
static_assert(EXPONENT_BIAS > 0);
167+
static_cast<uint32_t>(EXP_BIAS);
168+
LIBC_INLINE_VAR static constexpr BitsType SIGN_MASK = SIGN_MASK_;
169+
LIBC_INLINE_VAR static constexpr BitsType EXPONENT_MASK = EXP_MASK;
170+
LIBC_INLINE_VAR static constexpr BitsType EXP_MANT_MASK = EXP_MASK | SIG_MASK;
171+
172+
// If a number x is a NAN, then it is a quiet NAN if:
173+
// QuietNaNMask & bits(x) != 0
174+
// Else, it is a signalling NAN.
175+
static constexpr BitsType QUIET_NAN_MASK = QNAN_MASK;
99176
};
100177

101178
} // namespace internal
102179

103-
template <FPType> struct FPProperties {};
180+
template <FPType fp_type>
181+
struct FPProperties : public internal::FPCommonProperties<fp_type> {};
104182

105183
// ----------------
106184
// Work In Progress
@@ -110,90 +188,14 @@ template <FPType> struct FPProperties {};
110188
// empty, 'FPProperties' declaration can be fully replace with
111189
// 'FPCommonProperties' implementation.
112190

113-
template <>
114-
struct FPProperties<FPType::IEEE754_Binary32>
115-
: public internal::FPCommonProperties<FPType::IEEE754_Binary32> {
116-
// The mantissa precision includes the implicit bit.
117-
static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
118-
static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
119-
static constexpr BitsType SIGN_MASK = BitsType(1)
120-
<< (EXPONENT_WIDTH + MANTISSA_WIDTH);
121-
static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK);
122-
static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK + EXPONENT_MASK;
123-
static_assert(EXP_MANT_MASK == ~SIGN_MASK,
124-
"Exponent and mantissa masks are not as expected.");
125-
126-
// If a number x is a NAN, then it is a quiet NAN if:
127-
// QuietNaNMask & bits(x) != 0
128-
// Else, it is a signalling NAN.
129-
static constexpr BitsType QUIET_NAN_MASK = 0x00400000U;
130-
};
131-
132-
template <>
133-
struct FPProperties<FPType::IEEE754_Binary64>
134-
: public internal::FPCommonProperties<FPType::IEEE754_Binary64> {
135-
static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
136-
static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
137-
static constexpr BitsType SIGN_MASK = BitsType(1)
138-
<< (EXPONENT_WIDTH + MANTISSA_WIDTH);
139-
static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK);
140-
static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK + EXPONENT_MASK;
141-
static_assert(EXP_MANT_MASK == ~SIGN_MASK,
142-
"Exponent and mantissa masks are not as expected.");
143-
144-
// If a number x is a NAN, then it is a quiet NAN if:
145-
// QuietNaNMask & bits(x) != 0
146-
// Else, it is a signalling NAN.
147-
static constexpr BitsType QUIET_NAN_MASK = 0x0008000000000000ULL;
148-
};
149-
150191
// Properties for numbers represented in 80 bits long double on non-Windows x86
151192
// platforms.
152193
template <>
153194
struct FPProperties<FPType::X86_Binary80>
154195
: public internal::FPCommonProperties<FPType::X86_Binary80> {
155-
static constexpr BitsType FULL_WIDTH_MASK = ((BitsType(1) << BIT_WIDTH) - 1);
156-
static constexpr uint32_t MANTISSA_WIDTH = 63;
157-
static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
158-
static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
159196
// The x86 80 bit float represents the leading digit of the mantissa
160197
// explicitly. This is the mask for that bit.
161198
static constexpr BitsType EXPLICIT_BIT_MASK = (BitsType(1) << MANTISSA_WIDTH);
162-
static constexpr BitsType SIGN_MASK =
163-
BitsType(1) << (EXPONENT_WIDTH + MANTISSA_WIDTH + 1);
164-
static constexpr BitsType EXPONENT_MASK =
165-
((BitsType(1) << EXPONENT_WIDTH) - 1) << (MANTISSA_WIDTH + 1);
166-
static constexpr BitsType EXP_MANT_MASK =
167-
MANTISSA_MASK | EXPLICIT_BIT_MASK | EXPONENT_MASK;
168-
static_assert(EXP_MANT_MASK == (~SIGN_MASK & FULL_WIDTH_MASK),
169-
"Exponent and mantissa masks are not as expected.");
170-
171-
// If a number x is a NAN, then it is a quiet NAN if:
172-
// QuietNaNMask & bits(x) != 0
173-
// Else, it is a signalling NAN.
174-
static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
175-
<< (MANTISSA_WIDTH - 1);
176-
};
177-
178-
// Properties for numbers represented in 128 bits long double on non x86
179-
// platform.
180-
template <>
181-
struct FPProperties<FPType::IEEE754_Binary128>
182-
: public internal::FPCommonProperties<FPType::IEEE754_Binary128> {
183-
static constexpr uint32_t MANTISSA_PRECISION = MANTISSA_WIDTH + 1;
184-
static constexpr BitsType MANTISSA_MASK = (BitsType(1) << MANTISSA_WIDTH) - 1;
185-
static constexpr BitsType SIGN_MASK = BitsType(1)
186-
<< (EXPONENT_WIDTH + MANTISSA_WIDTH);
187-
static constexpr BitsType EXPONENT_MASK = ~(SIGN_MASK | MANTISSA_MASK);
188-
static constexpr BitsType EXP_MANT_MASK = MANTISSA_MASK | EXPONENT_MASK;
189-
static_assert(EXP_MANT_MASK == ~SIGN_MASK,
190-
"Exponent and mantissa masks are not as expected.");
191-
192-
// If a number x is a NAN, then it is a quiet NAN if:
193-
// QuietNaNMask & bits(x) != 0
194-
// Else, it is a signalling NAN.
195-
static constexpr BitsType QUIET_NAN_MASK = BitsType(1)
196-
<< (MANTISSA_WIDTH - 1);
197199
};
198200

199201
//-----------------------------------------------------------------------------

0 commit comments

Comments
 (0)