[libc][NFC] Simplify FPBits (#76835)
This patch reduces the scope of `FPBits` exported variables and functions. It also moves storage up into `FPRep` and tries to make the default and specialized versions of `FPBits` more uniform. The next step is to move the specialization from `FPBits` to `FPRep` so we can manipulate floating point representations through `FPType` alone - that is - independently from the host architecture.
This commit is contained in:
parent
79e62315be
commit
d02471ede5
@ -33,12 +33,6 @@ enum class FPType {
|
||||
|
||||
namespace internal {
|
||||
|
||||
// The type of encoding for supported floating point types.
|
||||
enum class FPEncoding {
|
||||
IEEE754,
|
||||
X86_ExtendedPrecision,
|
||||
};
|
||||
|
||||
// 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.
|
||||
@ -49,7 +43,6 @@ template <> struct FPLayout<FPType::IEEE754_Binary16> {
|
||||
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 FPLayout<FPType::IEEE754_Binary32> {
|
||||
@ -57,7 +50,6 @@ template <> struct FPLayout<FPType::IEEE754_Binary32> {
|
||||
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 FPLayout<FPType::IEEE754_Binary64> {
|
||||
@ -65,7 +57,6 @@ template <> struct FPLayout<FPType::IEEE754_Binary64> {
|
||||
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 FPLayout<FPType::IEEE754_Binary128> {
|
||||
@ -73,7 +64,6 @@ template <> struct FPLayout<FPType::IEEE754_Binary128> {
|
||||
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 FPLayout<FPType::X86_Binary80> {
|
||||
@ -81,15 +71,13 @@ template <> struct FPLayout<FPType::X86_Binary80> {
|
||||
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.
|
||||
// FPRepBase derives useful constants from the FPLayout.
|
||||
template <FPType fp_type>
|
||||
struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
|
||||
struct FPRepBase : public internal::FPLayout<fp_type> {
|
||||
private:
|
||||
using UP = internal::FPLayout<fp_type>;
|
||||
|
||||
@ -149,73 +137,44 @@ private:
|
||||
return StorageType(1) << position;
|
||||
}
|
||||
|
||||
public:
|
||||
// Merge bits from 'a' and 'b' values according to 'mask'.
|
||||
// Use 'a' bits when corresponding 'mask' bits are zeroes and 'b' bits when
|
||||
// corresponding bits are ones.
|
||||
LIBC_INLINE static constexpr StorageType merge(StorageType a, StorageType b,
|
||||
StorageType mask) {
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge
|
||||
return a ^ ((a ^ b) & mask);
|
||||
}
|
||||
|
||||
protected:
|
||||
// The number of bits after the decimal dot when the number is in normal form.
|
||||
LIBC_INLINE_VAR static constexpr int FRACTION_LEN =
|
||||
UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision ? SIG_LEN - 1
|
||||
: SIG_LEN;
|
||||
fp_type == FPType::X86_Binary80 ? SIG_LEN - 1 : SIG_LEN;
|
||||
LIBC_INLINE_VAR static constexpr uint32_t MANTISSA_PRECISION =
|
||||
FRACTION_LEN + 1;
|
||||
LIBC_INLINE_VAR static constexpr StorageType FRACTION_MASK =
|
||||
mask_trailing_ones<StorageType, FRACTION_LEN>();
|
||||
|
||||
protected:
|
||||
// If a number x is a NAN, then it is a quiet NAN if:
|
||||
// QUIET_NAN_MASK & bits(x) != 0
|
||||
LIBC_INLINE_VAR static constexpr StorageType QUIET_NAN_MASK =
|
||||
UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision
|
||||
fp_type == FPType::X86_Binary80
|
||||
? bit_at(SIG_LEN - 1) | bit_at(SIG_LEN - 2) // 0b1100...
|
||||
: bit_at(SIG_LEN - 1); // 0b1000...
|
||||
|
||||
// If a number x is a NAN, then it is a signalling NAN if:
|
||||
// SIGNALING_NAN_MASK & bits(x) != 0
|
||||
LIBC_INLINE_VAR static constexpr StorageType SIGNALING_NAN_MASK =
|
||||
UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision
|
||||
fp_type == FPType::X86_Binary80
|
||||
? bit_at(SIG_LEN - 1) | bit_at(SIG_LEN - 3) // 0b1010...
|
||||
: bit_at(SIG_LEN - 2); // 0b0100...
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// This is a temporary class to unify common methods and properties between
|
||||
// FPBits and FPBits<long double>.
|
||||
template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
|
||||
using UP = FPBaseMasksAndShifts<fp_type>;
|
||||
using typename UP::StorageType;
|
||||
using UP::TOTAL_LEN;
|
||||
|
||||
protected:
|
||||
using UP::EXP_SIG_MASK;
|
||||
using UP::QUIET_NAN_MASK;
|
||||
// The floating point number representation as an unsigned integer.
|
||||
StorageType bits = 0;
|
||||
|
||||
public:
|
||||
using UP::EXP_BIAS;
|
||||
using UP::EXP_LEN;
|
||||
using UP::EXP_MASK;
|
||||
using UP::EXP_MASK_SHIFT;
|
||||
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 FPRep() : bits(0) {}
|
||||
LIBC_INLINE explicit constexpr FPRep(StorageType bits) : bits(bits) {}
|
||||
|
||||
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
|
||||
mantVal &= FRACTION_MASK;
|
||||
bits &= ~FRACTION_MASK;
|
||||
bits |= mantVal;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr StorageType get_mantissa() const {
|
||||
return bits & FRACTION_MASK;
|
||||
LIBC_INLINE constexpr bool get_sign() const {
|
||||
return (bits & SIGN_MASK) != 0;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr void set_sign(bool signVal) {
|
||||
@ -223,21 +182,22 @@ public:
|
||||
bits ^= SIGN_MASK;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr bool get_sign() const {
|
||||
return (bits & SIGN_MASK) != 0;
|
||||
LIBC_INLINE constexpr StorageType get_mantissa() const {
|
||||
return bits & FRACTION_MASK;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
|
||||
// clear exponent bits
|
||||
bits &= ~EXP_MASK;
|
||||
// set exponent bits
|
||||
bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
|
||||
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
|
||||
bits = merge(bits, mantVal, FRACTION_MASK);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
|
||||
return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
|
||||
bits = merge(bits, biased << EXP_MASK_SHIFT, EXP_MASK);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr int get_exponent() const {
|
||||
return int(get_biased_exponent()) - EXP_BIAS;
|
||||
}
|
||||
@ -266,6 +226,23 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
// Manipulates the representation of a floating point number defined by its
|
||||
// FPType. This layer is architecture agnostic and does not handle C++ floating
|
||||
// point types directly ('float', 'double' and 'long double'). Use the FPBits
|
||||
// below if needed.
|
||||
//
|
||||
// TODO: Specialize this class for FPType::X86_Binary80 and remove ad-hoc logic
|
||||
// from FPRepBase.
|
||||
template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
|
||||
using UP = FPRepBase<fp_type>;
|
||||
using typename UP::StorageType;
|
||||
using UP::FRACTION_LEN;
|
||||
using UP::FRACTION_MASK;
|
||||
using UP::MANTISSA_PRECISION;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
// 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>()> {
|
||||
static_assert(cpp::is_floating_point_v<T>,
|
||||
"FPBits instantiated with invalid type.");
|
||||
using UP = internal::FPRep<get_fp_type<T>()>;
|
||||
using StorageType = typename UP::StorageType;
|
||||
using UP::bits;
|
||||
|
||||
private:
|
||||
using UP::EXP_SIG_MASK;
|
||||
using UP::QUIET_NAN_MASK;
|
||||
using UP::SIG_LEN;
|
||||
using UP::SIG_MASK;
|
||||
|
||||
public:
|
||||
using StorageType = typename UP::StorageType;
|
||||
using UP::bits;
|
||||
using UP::EXP_BIAS;
|
||||
using UP::EXP_LEN;
|
||||
using UP::EXP_MASK;
|
||||
@ -327,9 +306,37 @@ public:
|
||||
using UP::FRACTION_MASK;
|
||||
using UP::SIGN_MASK;
|
||||
using UP::TOTAL_LEN;
|
||||
using UP::UP;
|
||||
|
||||
using UP::get_biased_exponent;
|
||||
using UP::is_zero;
|
||||
// Constants.
|
||||
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
|
||||
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
|
||||
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
|
||||
static constexpr StorageType MIN_NORMAL = (StorageType(1) << FRACTION_LEN);
|
||||
static constexpr StorageType MAX_NORMAL =
|
||||
(StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
|
||||
|
||||
// Constructors.
|
||||
LIBC_INLINE constexpr FPBits() = default;
|
||||
|
||||
template <typename XType> LIBC_INLINE constexpr explicit FPBits(XType x) {
|
||||
using Unqual = typename cpp::remove_cv_t<XType>;
|
||||
if constexpr (cpp::is_same_v<Unqual, T>) {
|
||||
bits = cpp::bit_cast<StorageType>(x);
|
||||
} else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
|
||||
bits = x;
|
||||
} else {
|
||||
// We don't want accidental type promotions/conversions, so we require
|
||||
// exact type match.
|
||||
static_assert(cpp::always_false<XType>);
|
||||
}
|
||||
}
|
||||
// Floating-point conversions.
|
||||
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
|
||||
|
||||
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
|
||||
|
||||
// The function return mantissa with the implicit bit set iff the current
|
||||
// value is a valid normal number.
|
||||
@ -340,33 +347,6 @@ public:
|
||||
(FRACTION_MASK & bits);
|
||||
}
|
||||
|
||||
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
|
||||
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
|
||||
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
|
||||
static constexpr StorageType MIN_NORMAL = (StorageType(1) << FRACTION_LEN);
|
||||
static constexpr StorageType MAX_NORMAL =
|
||||
((StorageType(MAX_BIASED_EXPONENT) - 1) << FRACTION_LEN) | MAX_SUBNORMAL;
|
||||
|
||||
// We don't want accidental type promotions/conversions, so we require exact
|
||||
// type match.
|
||||
template <typename XType, cpp::enable_if_t<cpp::is_same_v<T, XType>, int> = 0>
|
||||
LIBC_INLINE constexpr explicit FPBits(XType x)
|
||||
: UP(cpp::bit_cast<StorageType>(x)) {}
|
||||
|
||||
template <typename XType,
|
||||
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
|
||||
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
|
||||
|
||||
LIBC_INLINE constexpr FPBits() : UP() {}
|
||||
|
||||
LIBC_INLINE constexpr void set_val(T value) {
|
||||
bits = cpp::bit_cast<StorageType>(value);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
|
||||
|
||||
LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
|
||||
|
||||
LIBC_INLINE constexpr bool is_inf() const {
|
||||
return (bits & EXP_SIG_MASK) == EXP_MASK;
|
||||
}
|
||||
@ -387,14 +367,22 @@ public:
|
||||
return FPBits(bits & EXP_SIG_MASK);
|
||||
}
|
||||
|
||||
// Methods below this are used by tests.
|
||||
|
||||
LIBC_INLINE static constexpr T zero(bool sign = false) {
|
||||
return FPBits(sign ? SIGN_MASK : StorageType(0)).get_val();
|
||||
StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
|
||||
| 0 // exponent
|
||||
| 0; // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr T neg_zero() { return zero(true); }
|
||||
|
||||
LIBC_INLINE static constexpr T inf(bool sign = false) {
|
||||
return FPBits((sign ? SIGN_MASK : StorageType(0)) | EXP_MASK).get_val();
|
||||
StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
|
||||
| EXP_MASK // exponent
|
||||
| 0; // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr T neg_inf() { return inf(true); }
|
||||
@ -416,15 +404,24 @@ public:
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr T build_nan(StorageType v) {
|
||||
FPBits<T> bits(inf());
|
||||
bits.set_mantissa(v);
|
||||
return T(bits);
|
||||
StorageType rep = 0 // sign
|
||||
| EXP_MASK // exponent
|
||||
| (v & FRACTION_MASK); // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr T build_quiet_nan(StorageType v) {
|
||||
return build_nan(QUIET_NAN_MASK | v);
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr FPBits<T>
|
||||
create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
|
||||
StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
|
||||
| ((biased_exp << EXP_MASK_SHIFT) & EXP_MASK) // exponent
|
||||
| (mantissa & FRACTION_MASK); // mantissa
|
||||
return FPBits(rep);
|
||||
}
|
||||
|
||||
// The function convert integer number and unbiased exponent to proper float
|
||||
// T type:
|
||||
// Result = number * 2^(ep+1 - exponent_bias)
|
||||
@ -452,15 +449,6 @@ public:
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr FPBits<T>
|
||||
create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
|
||||
FPBits<T> result;
|
||||
result.set_sign(sign);
|
||||
result.set_biased_exponent(biased_exp);
|
||||
result.set_mantissa(mantissa);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace fputil
|
||||
|
||||
@ -45,7 +45,7 @@ template <typename T> LIBC_INLINE cpp::string str(fputil::FPBits<T> x) {
|
||||
|
||||
cpp::string s;
|
||||
|
||||
const details::ZeroPaddedHexFmt<StorageType> bits(x.bits);
|
||||
const details::ZeroPaddedHexFmt<StorageType> bits(x.uintval());
|
||||
s += bits.view();
|
||||
|
||||
s += " = (S: ";
|
||||
|
||||
@ -30,70 +30,69 @@ template <>
|
||||
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;
|
||||
|
||||
private:
|
||||
using UP::bits;
|
||||
using UP::EXP_SIG_MASK;
|
||||
using UP::QUIET_NAN_MASK;
|
||||
|
||||
public:
|
||||
using UP::EXP_BIAS;
|
||||
using UP::EXP_LEN;
|
||||
using UP::EXP_MASK;
|
||||
using UP::EXP_MASK_SHIFT;
|
||||
using UP::FP_MASK;
|
||||
using UP::FRACTION_LEN;
|
||||
using UP::FRACTION_MASK;
|
||||
using UP::SIGN_MASK;
|
||||
using UP::TOTAL_LEN;
|
||||
|
||||
static constexpr int MAX_BIASED_EXPONENT = 0x7FFF;
|
||||
// Constants.
|
||||
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
|
||||
// The x86 80 bit float represents the leading digit of the mantissa
|
||||
// explicitly. This is the mask for that bit.
|
||||
static constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1)
|
||||
<< FRACTION_LEN;
|
||||
// The X80 significand is made of an explicit bit and the fractional part.
|
||||
static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0,
|
||||
"the explicit bit and the fractional part should not overlap");
|
||||
static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK,
|
||||
"the explicit bit and the fractional part should cover the "
|
||||
"whole significand");
|
||||
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
|
||||
// Subnormal numbers include the implicit bit in x86 long double formats.
|
||||
static constexpr StorageType MAX_SUBNORMAL =
|
||||
(StorageType(1) << FRACTION_LEN) - 1;
|
||||
static constexpr StorageType MIN_NORMAL = (StorageType(3) << FRACTION_LEN);
|
||||
static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
|
||||
static constexpr StorageType MIN_NORMAL =
|
||||
(StorageType(1) << SIG_LEN) | EXPLICIT_BIT_MASK;
|
||||
static constexpr StorageType MAX_NORMAL =
|
||||
(StorageType(MAX_BIASED_EXPONENT - 1) << (FRACTION_LEN + 1)) |
|
||||
(StorageType(1) << FRACTION_LEN) | MAX_SUBNORMAL;
|
||||
(StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
|
||||
|
||||
LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
|
||||
// The x86 80 bit float represents the leading digit of the mantissa
|
||||
// explicitly. This is the mask for that bit.
|
||||
constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1) << FRACTION_LEN;
|
||||
return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
|
||||
// Constructors.
|
||||
LIBC_INLINE constexpr FPBits() = default;
|
||||
|
||||
template <typename XType> LIBC_INLINE constexpr explicit FPBits(XType x) {
|
||||
using Unqual = typename cpp::remove_cv_t<XType>;
|
||||
if constexpr (cpp::is_same_v<Unqual, long double>) {
|
||||
bits = cpp::bit_cast<StorageType>(x);
|
||||
} else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
|
||||
bits = x;
|
||||
} else {
|
||||
// We don't want accidental type promotions/conversions, so we require
|
||||
// exact type match.
|
||||
static_assert(cpp::always_false<XType>);
|
||||
}
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
|
||||
bits &= ~(StorageType(1) << FRACTION_LEN);
|
||||
bits |= (StorageType(implicitVal) << FRACTION_LEN);
|
||||
// Floating-point conversions.
|
||||
LIBC_INLINE constexpr long double get_val() const {
|
||||
return cpp::bit_cast<long double>(bits);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr operator long double() const {
|
||||
return cpp::bit_cast<long double>(bits);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
|
||||
return bits & SIG_MASK;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr bool get_implicit_bit() const {
|
||||
return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
|
||||
return bits & EXPLICIT_BIT_MASK;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr FPBits() : UP() {}
|
||||
|
||||
template <typename XType,
|
||||
cpp::enable_if_t<cpp::is_same_v<long double, XType>, int> = 0>
|
||||
LIBC_INLINE constexpr explicit FPBits(XType x)
|
||||
: UP(cpp::bit_cast<StorageType>(x)) {
|
||||
// bits starts uninitialized, and setting it to a long double only
|
||||
// overwrites the first 80 bits. This clears those upper bits.
|
||||
bits = bits & ((StorageType(1) << 80) - 1);
|
||||
}
|
||||
|
||||
template <typename XType,
|
||||
cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
|
||||
LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
|
||||
|
||||
LIBC_INLINE constexpr operator long double() {
|
||||
return cpp::bit_cast<long double>(bits);
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr long double get_val() const {
|
||||
return cpp::bit_cast<long double>(bits);
|
||||
LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
|
||||
if (get_implicit_bit() != implicitVal)
|
||||
bits ^= EXPLICIT_BIT_MASK;
|
||||
}
|
||||
|
||||
LIBC_INLINE constexpr bool is_inf() const {
|
||||
@ -117,34 +116,26 @@ public:
|
||||
|
||||
// Methods below this are used by tests.
|
||||
|
||||
LIBC_INLINE static constexpr long double zero() { return 0.0l; }
|
||||
LIBC_INLINE static constexpr long double zero(bool sign = false) {
|
||||
StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
|
||||
| 0 // exponent
|
||||
| 0 // explicit bit
|
||||
| 0; // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr long double neg_zero() { return -0.0l; }
|
||||
LIBC_INLINE static constexpr long double neg_zero() { return zero(true); }
|
||||
|
||||
LIBC_INLINE static constexpr long double inf(bool sign = false) {
|
||||
FPBits<long double> bits(0.0l);
|
||||
bits.set_biased_exponent(MAX_BIASED_EXPONENT);
|
||||
bits.set_implicit_bit(1);
|
||||
if (sign) {
|
||||
bits.set_sign(true);
|
||||
}
|
||||
return bits.get_val();
|
||||
StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
|
||||
| EXP_MASK // exponent
|
||||
| EXPLICIT_BIT_MASK // explicit bit
|
||||
| 0; // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr long double neg_inf() { return inf(true); }
|
||||
|
||||
LIBC_INLINE static constexpr long double build_nan(StorageType v) {
|
||||
FPBits<long double> bits(0.0l);
|
||||
bits.set_biased_exponent(MAX_BIASED_EXPONENT);
|
||||
bits.set_implicit_bit(1);
|
||||
bits.set_mantissa(v);
|
||||
return bits;
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) {
|
||||
return build_nan(QUIET_NAN_MASK | v);
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr long double min_normal() {
|
||||
return FPBits(MIN_NORMAL).get_val();
|
||||
}
|
||||
@ -161,13 +152,16 @@ public:
|
||||
return FPBits(MAX_SUBNORMAL).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr FPBits<long double>
|
||||
create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
|
||||
FPBits<long double> result;
|
||||
result.set_sign(sign);
|
||||
result.set_biased_exponent(biased_exp);
|
||||
result.set_mantissa(mantissa);
|
||||
return result;
|
||||
LIBC_INLINE static constexpr long double build_nan(StorageType v) {
|
||||
StorageType rep = 0 // sign
|
||||
| EXP_MASK // exponent
|
||||
| EXPLICIT_BIT_MASK // explicit bit
|
||||
| (v & FRACTION_MASK); // mantissa
|
||||
return FPBits(rep).get_val();
|
||||
}
|
||||
|
||||
LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) {
|
||||
return build_nan(QUIET_NAN_MASK | v);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -758,7 +758,7 @@ LLVM_LIBC_FUNCTION(double, log, (double x)) {
|
||||
return x;
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(x * 0x1.0p52);
|
||||
xbits = FPBits_t(x * 0x1.0p52);
|
||||
x_e -= 52;
|
||||
x_u = xbits.uintval();
|
||||
}
|
||||
|
||||
@ -759,7 +759,7 @@ LLVM_LIBC_FUNCTION(double, log10, (double x)) {
|
||||
return x;
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(x * 0x1.0p52);
|
||||
xbits = FPBits_t(x * 0x1.0p52);
|
||||
x_e -= 52;
|
||||
x_u = xbits.uintval();
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ LLVM_LIBC_FUNCTION(float, log10f, (float x)) {
|
||||
return x;
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(xbits.get_val() * 0x1.0p23f);
|
||||
xbits = FPBits(xbits.get_val() * 0x1.0p23f);
|
||||
m -= 23;
|
||||
x_u = xbits.uintval();
|
||||
}
|
||||
|
||||
@ -880,7 +880,7 @@ LLVM_LIBC_FUNCTION(double, log2, (double x)) {
|
||||
return x;
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(x * 0x1.0p52);
|
||||
xbits = FPBits_t(x * 0x1.0p52);
|
||||
x_e -= 52;
|
||||
x_u = xbits.uintval();
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ LLVM_LIBC_FUNCTION(float, log2f, (float x)) {
|
||||
return x;
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(xbits.get_val() * 0x1.0p23f);
|
||||
xbits = FPBits(xbits.get_val() * 0x1.0p23f);
|
||||
m -= 23;
|
||||
}
|
||||
|
||||
|
||||
@ -87,7 +87,7 @@ LLVM_LIBC_FUNCTION(float, logf, (float x)) {
|
||||
return static_cast<float>(FPBits::neg_inf());
|
||||
}
|
||||
// Normalize denormal inputs.
|
||||
xbits.set_val(xbits.get_val() * 0x1.0p23f);
|
||||
xbits = FPBits(xbits.get_val() * 0x1.0p23f);
|
||||
m -= 23;
|
||||
x_u = xbits.uintval();
|
||||
}
|
||||
|
||||
@ -87,7 +87,7 @@ public:
|
||||
|
||||
EXPECT_EQ(str_end - inputString, expectedStrLen);
|
||||
|
||||
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
|
||||
EXPECT_EQ(actual_fp.uintval(), expected_fp.uintval());
|
||||
EXPECT_EQ(actual_fp.get_sign(), expected_fp.get_sign());
|
||||
EXPECT_EQ(actual_fp.get_exponent(), expected_fp.get_exponent());
|
||||
EXPECT_EQ(actual_fp.get_mantissa(), expected_fp.get_mantissa());
|
||||
|
||||
@ -31,7 +31,7 @@ TEST(LlvmLibcDifftime, SmokeTest) {
|
||||
actual_fp = LIBC_NAMESPACE::fputil::FPBits<long double>(
|
||||
static_cast<long double>(result));
|
||||
|
||||
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
|
||||
EXPECT_EQ(actual_fp.uintval(), expected_fp.uintval());
|
||||
EXPECT_EQ(actual_fp.get_sign(), expected_fp.get_sign());
|
||||
EXPECT_EQ(actual_fp.get_exponent(), expected_fp.get_exponent());
|
||||
EXPECT_EQ(actual_fp.get_mantissa(), expected_fp.get_mantissa());
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user