Skip to content

Commit

Permalink
[libc][NFC] Remove FloatProperties (#76508)
Browse files Browse the repository at this point in the history
Access is now done through `FPBits` exclusively.
This patch also renames a few internal structs and uses `T` instead of
`FP` as a template parameter.
  • Loading branch information
gchatelet committed Jan 3, 2024
1 parent b7d5b0d commit c09e690
Show file tree
Hide file tree
Showing 26 changed files with 180 additions and 209 deletions.
7 changes: 3 additions & 4 deletions libc/fuzzing/stdlib/strtofloat_fuzz.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,17 @@

#include "utils/MPFRWrapper/mpfr_inc.h"

using LIBC_NAMESPACE::fputil::FloatProperties;
using LIBC_NAMESPACE::fputil::FPBits;

// This function calculates the effective precision for a given float type and
// exponent. Subnormals have a lower effective precision since they don't
// necessarily use all of the bits of the mantissa.
template <typename F> inline constexpr int effective_precision(int exponent) {
const int full_precision = FloatProperties<F>::MANTISSA_PRECISION;
const int full_precision = FPBits<F>::MANTISSA_PRECISION;

// This is intended to be 0 when the exponent is the lowest normal and
// increase as the exponent's magnitude increases.
const int bits_below_normal =
(-exponent) - (FloatProperties<F>::EXP_BIAS - 1);
const int bits_below_normal = (-exponent) - (FPBits<F>::EXP_BIAS - 1);

// The precision should be the normal, full precision, minus the bits lost
// by this being a subnormal, minus one for the implicit leading one.
Expand Down
131 changes: 66 additions & 65 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,64 +39,66 @@ enum class FPEncoding {
X86_ExtendedPrecision,
};

template <FPType> struct FPBaseProperties {};
// Defines the layout (sign, exponent, significand) of a floating point type in
// memory. It also defines its associated StorageType, i.e., the unsigned
// integer type used to manipulate its representation.
template <FPType> struct FPLayout {};

template <> struct FPBaseProperties<FPType::IEEE754_Binary16> {
template <> struct FPLayout<FPType::IEEE754_Binary16> {
using StorageType = uint16_t;
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = 16;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 10;
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 5;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 10;
LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
};

template <> struct FPBaseProperties<FPType::IEEE754_Binary32> {
template <> struct FPLayout<FPType::IEEE754_Binary32> {
using StorageType = uint32_t;
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = 32;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 23;
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 8;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 23;
LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
};

template <> struct FPBaseProperties<FPType::IEEE754_Binary64> {
template <> struct FPLayout<FPType::IEEE754_Binary64> {
using StorageType = uint64_t;
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = 64;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 52;
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 11;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 52;
LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
};

template <> struct FPBaseProperties<FPType::IEEE754_Binary128> {
template <> struct FPLayout<FPType::IEEE754_Binary128> {
using StorageType = UInt128;
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = 128;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 112;
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 112;
LIBC_INLINE_VAR static constexpr auto ENCODING = FPEncoding::IEEE754;
};

template <> struct FPBaseProperties<FPType::X86_Binary80> {
template <> struct FPLayout<FPType::X86_Binary80> {
using StorageType = UInt128;
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = 80;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
LIBC_INLINE_VAR static constexpr int EXP_LEN = 15;
LIBC_INLINE_VAR static constexpr int SIG_LEN = 64;
LIBC_INLINE_VAR static constexpr auto ENCODING =
FPEncoding::X86_ExtendedPrecision;
};

} // namespace internal

// FPBaseMasksAndShifts derives useful constants from the FPLayout.
template <FPType fp_type>
struct FPProperties : public internal::FPBaseProperties<fp_type> {
struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
private:
using UP = internal::FPBaseProperties<fp_type>;
using UP = internal::FPLayout<fp_type>;

public:
// The number of bits to represent sign. For documentation purpose, always 1.
LIBC_INLINE_VAR static constexpr int SIGN_LEN = 1;
using UP::EXP_LEN; // The number of bits for the *exponent* part
using UP::SIG_LEN; // The number of bits for the *significand* part
using UP::TOTAL_LEN; // For convenience, the sum of `SIG_LEN`, `EXP_LEN`,
// and `SIGN_LEN`.
static_assert(SIGN_LEN + EXP_LEN + SIG_LEN == TOTAL_LEN);
using UP::EXP_LEN; // The number of bits for the *exponent* part
using UP::SIG_LEN; // The number of bits for the *significand* part
using UP::SIGN_LEN; // The number of bits for the *sign* part
// For convenience, the sum of `SIG_LEN`, `EXP_LEN`, and `SIGN_LEN`.
LIBC_INLINE_VAR static constexpr int TOTAL_LEN = SIGN_LEN + EXP_LEN + SIG_LEN;

// An unsigned integer that is wide enough to contain all of the floating
// point bits.
Expand Down Expand Up @@ -173,45 +175,12 @@ struct FPProperties : public internal::FPBaseProperties<fp_type> {
: bit_at(SIG_LEN - 2); // 0b0100...
};

//-----------------------------------------------------------------------------
template <typename FP> LIBC_INLINE static constexpr FPType get_fp_type() {
if constexpr (cpp::is_same_v<FP, float> && __FLT_MANT_DIG__ == 24)
return FPType::IEEE754_Binary32;
else if constexpr (cpp::is_same_v<FP, double> && __DBL_MANT_DIG__ == 53)
return FPType::IEEE754_Binary64;
else if constexpr (cpp::is_same_v<FP, long double>) {
if constexpr (__LDBL_MANT_DIG__ == 53)
return FPType::IEEE754_Binary64;
else if constexpr (__LDBL_MANT_DIG__ == 64)
return FPType::X86_Binary80;
else if constexpr (__LDBL_MANT_DIG__ == 113)
return FPType::IEEE754_Binary128;
}
#if defined(LIBC_COMPILER_HAS_C23_FLOAT16)
else if constexpr (cpp::is_same_v<FP, _Float16>)
return FPType::IEEE754_Binary16;
#endif
#if defined(LIBC_COMPILER_HAS_C23_FLOAT128)
else if constexpr (cpp::is_same_v<FP, _Float128>)
return FPType::IEEE754_Binary128;
#endif
#if defined(LIBC_COMPILER_HAS_FLOAT128_EXTENSION)
else if constexpr (cpp::is_same_v<FP, __float128>)
return FPType::IEEE754_Binary128;
#endif
else
static_assert(cpp::always_false<FP>, "Unsupported type");
}

template <typename FP>
struct FloatProperties : public FPProperties<get_fp_type<FP>()> {};

namespace internal {

// This is a temporary class to unify common methods and properties between
// FPBits and FPBits<long double>.
template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
using UP = FPProperties<fp_type>;
template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
using UP = FPBaseMasksAndShifts<fp_type>;
using typename UP::StorageType;
using UP::TOTAL_LEN;

Expand All @@ -227,15 +196,17 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
using UP::FP_MASK;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::MANTISSA_PRECISION;
using UP::SIGN_MASK;
using UP::STORAGE_LEN;

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

LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
LIBC_INLINE constexpr FPRep() : bits(0) {}
LIBC_INLINE explicit constexpr FPRep(StorageType bits) : bits(bits) {}

LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
mantVal &= FRACTION_MASK;
Expand Down Expand Up @@ -297,6 +268,37 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {

} // namespace internal

// Returns the FPType corresponding to C++ type T on the host.
template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
using UnqualT = cpp::remove_cv_t<T>;
if constexpr (cpp::is_same_v<UnqualT, float> && __FLT_MANT_DIG__ == 24)
return FPType::IEEE754_Binary32;
else if constexpr (cpp::is_same_v<UnqualT, double> && __DBL_MANT_DIG__ == 53)
return FPType::IEEE754_Binary64;
else if constexpr (cpp::is_same_v<UnqualT, long double>) {
if constexpr (__LDBL_MANT_DIG__ == 53)
return FPType::IEEE754_Binary64;
else if constexpr (__LDBL_MANT_DIG__ == 64)
return FPType::X86_Binary80;
else if constexpr (__LDBL_MANT_DIG__ == 113)
return FPType::IEEE754_Binary128;
}
#if defined(LIBC_COMPILER_HAS_C23_FLOAT16)
else if constexpr (cpp::is_same_v<UnqualT, _Float16>)
return FPType::IEEE754_Binary16;
#endif
#if defined(LIBC_COMPILER_HAS_C23_FLOAT128)
else if constexpr (cpp::is_same_v<UnqualT, _Float128>)
return FPType::IEEE754_Binary128;
#endif
#if defined(LIBC_COMPILER_HAS_FLOAT128_EXTENSION)
else if constexpr (cpp::is_same_v<UnqualT, __float128>)
return FPType::IEEE754_Binary128;
#endif
else
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}

// A generic class to represent single precision, double precision, and quad
// precision IEEE 754 floating point formats.
// On most platforms, the 'float' type corresponds to single precision floating
Expand All @@ -305,11 +307,10 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
// floating numbers. On x86 platforms however, the 'long double' type maps to
// an x87 floating point format. This format is an IEEE 754 extension format.
// It is handled as an explicit specialization of this class.
template <typename T>
struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
using UP = internal::FPBitsCommon<get_fp_type<T>()>;
using UP = internal::FPRep<get_fp_type<T>()>;
using StorageType = typename UP::StorageType;
using UP::bits;

Expand Down
6 changes: 3 additions & 3 deletions libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,13 +174,13 @@ LIBC_INLINE T nextafter(T from, U to) {
} else {
int_val = FPBits<T>::MIN_SUBNORMAL;
if (to_bits.get_sign())
int_val |= FloatProperties<T>::SIGN_MASK;
int_val |= FPBits<T>::SIGN_MASK;
}

StorageType exponent_bits = int_val & FloatProperties<T>::EXP_MASK;
StorageType exponent_bits = int_val & FPBits<T>::EXP_MASK;
if (exponent_bits == StorageType(0))
raise_except_if_required(FE_UNDERFLOW | FE_INEXACT);
else if (exponent_bits == FloatProperties<T>::EXP_MASK)
else if (exponent_bits == FPBits<T>::EXP_MASK)
raise_except_if_required(FE_OVERFLOW | FE_INEXACT);

return cpp::bit_cast<T>(int_val);
Expand Down
37 changes: 17 additions & 20 deletions libc/src/__support/FPUtil/dyadic_float.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ template <size_t Bits> struct DyadicFloat {

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
DyadicFloat(T x) {
static_assert(FloatProperties<T>::FRACTION_LEN < Bits);
static_assert(FPBits<T>::FRACTION_LEN < Bits);
FPBits<T> x_bits(x);
sign = x_bits.get_sign();
exponent = x_bits.get_exponent() - FloatProperties<T>::FRACTION_LEN;
exponent = x_bits.get_exponent() - FPBits<T>::FRACTION_LEN;
mantissa = MantissaType(x_bits.get_explicit_mantissa());
normalize();
}
Expand Down Expand Up @@ -83,21 +83,20 @@ template <size_t Bits> struct DyadicFloat {
// Output is rounded correctly with respect to the current rounding mode.
// TODO(lntue): Add support for underflow.
// TODO(lntue): Test or add specialization for x86 long double.
template <typename T, typename = cpp::enable_if_t<
cpp::is_floating_point_v<T> &&
(FloatProperties<T>::FRACTION_LEN < Bits),
void>>
template <typename T,
typename = cpp::enable_if_t<cpp::is_floating_point_v<T> &&
(FPBits<T>::FRACTION_LEN < Bits),
void>>
explicit operator T() const {
// TODO(lntue): Do we need to treat signed zeros properly?
if (mantissa.is_zero())
return 0.0;

// Assume that it is normalized, and output is also normal.
constexpr uint32_t PRECISION = FloatProperties<T>::MANTISSA_PRECISION;
constexpr uint32_t PRECISION = FPBits<T>::MANTISSA_PRECISION;
using output_bits_t = typename FPBits<T>::StorageType;

int exp_hi =
exponent + static_cast<int>((Bits - 1) + FloatProperties<T>::EXP_BIAS);
int exp_hi = exponent + static_cast<int>((Bits - 1) + FPBits<T>::EXP_BIAS);

bool denorm = false;
uint32_t shift = Bits - PRECISION;
Expand All @@ -106,7 +105,7 @@ template <size_t Bits> struct DyadicFloat {
denorm = true;
shift = (Bits - PRECISION) + static_cast<uint32_t>(1 - exp_hi);

exp_hi = FloatProperties<T>::EXP_BIAS;
exp_hi = FPBits<T>::EXP_BIAS;
}

int exp_lo = exp_hi - static_cast<int>(PRECISION) - 1;
Expand All @@ -115,7 +114,7 @@ template <size_t Bits> struct DyadicFloat {

T d_hi = FPBits<T>::create_value(sign, exp_hi,
static_cast<output_bits_t>(m_hi) &
FloatProperties<T>::FRACTION_MASK)
FPBits<T>::FRACTION_MASK)
.get_val();

const MantissaType round_mask = MantissaType(1) << (shift - 1);
Expand All @@ -129,15 +128,13 @@ template <size_t Bits> struct DyadicFloat {
if (LIBC_UNLIKELY(exp_lo <= 0)) {
// d_lo is denormal, but the output is normal.
int scale_up_exponent = 2 * PRECISION;
T scale_up_factor = FPBits<T>::create_value(sign,
FloatProperties<T>::EXP_BIAS +
scale_up_exponent,
output_bits_t(0))
.get_val();
T scale_up_factor =
FPBits<T>::create_value(sign, FPBits<T>::EXP_BIAS + scale_up_exponent,
output_bits_t(0))
.get_val();
T scale_down_factor =
FPBits<T>::create_value(
sign, FloatProperties<T>::EXP_BIAS - scale_up_exponent,
output_bits_t(0))
FPBits<T>::create_value(sign, FPBits<T>::EXP_BIAS - scale_up_exponent,
output_bits_t(0))
.get_val();

d_lo = FPBits<T>::create_value(sign, exp_lo + scale_up_exponent,
Expand All @@ -156,7 +153,7 @@ template <size_t Bits> struct DyadicFloat {
if (LIBC_UNLIKELY(denorm)) {
// Output is denormal, simply clear the exponent field.
output_bits_t clear_exp = output_bits_t(exp_hi)
<< FloatProperties<T>::FRACTION_LEN;
<< FPBits<T>::FRACTION_LEN;
output_bits_t r_bits = FPBits<T>(r).uintval() - clear_exp;
return FPBits<T>(r_bits).get_val();
}
Expand Down
7 changes: 3 additions & 4 deletions libc/src/__support/FPUtil/generic/FMA.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ LIBC_INLINE bool shift_mantissa(int shift_length, UInt128 &mant) {

template <> LIBC_INLINE double fma<double>(double x, double y, double z) {
using FPBits = fputil::FPBits<double>;
using FloatProp = fputil::FloatProperties<double>;

if (LIBC_UNLIKELY(x == 0 || y == 0 || z == 0)) {
return x * y + z;
Expand Down Expand Up @@ -267,10 +266,10 @@ template <> LIBC_INLINE double fma<double>(double x, double y, double z) {
}

// Remove hidden bit and append the exponent field and sign bit.
result = (result & FloatProp::FRACTION_MASK) |
(static_cast<uint64_t>(r_exp) << FloatProp::FRACTION_LEN);
result = (result & FPBits::FRACTION_MASK) |
(static_cast<uint64_t>(r_exp) << FPBits::FRACTION_LEN);
if (prod_sign) {
result |= FloatProp::SIGN_MASK;
result |= FPBits::SIGN_MASK;
}

// Rounding.
Expand Down
5 changes: 2 additions & 3 deletions libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,8 @@ namespace LIBC_NAMESPACE {
namespace fputil {

template <>
struct FPBits<long double>
: public internal::FPBitsCommon<FPType::X86_Binary80> {
using UP = internal::FPBitsCommon<FPType::X86_Binary80>;
struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
using UP = internal::FPRep<FPType::X86_Binary80>;
using StorageType = typename UP::StorageType;
using UP::bits;

Expand Down
Loading

0 comments on commit c09e690

Please sign in to comment.