[libc-commits] [libc] 25ab2fc - [libc][NFC] Make FPRep more testable (#80453)
via libc-commits
libc-commits at lists.llvm.org
Mon Feb 5 01:26:48 PST 2024
Author: Guillaume Chatelet
Date: 2024-02-05T10:26:44+01:00
New Revision: 25ab2fc06b6780335c291fa1fc23c2aec01c34a6
URL: https://github.com/llvm/llvm-project/commit/25ab2fc06b6780335c291fa1fc23c2aec01c34a6
DIFF: https://github.com/llvm/llvm-project/commit/25ab2fc06b6780335c291fa1fc23c2aec01c34a6.diff
LOG: [libc][NFC] Make FPRep more testable (#80453)
Added:
Modified:
libc/src/__support/FPUtil/FPBits.h
libc/test/src/__support/FPUtil/fpbits_test.cpp
Removed:
################################################################################
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index d975638be348d..7bfdfc1691a2d 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -75,27 +75,32 @@ LIBC_INLINE_VAR constexpr Sign Sign::POS = Sign(false);
// │ │
// └────────────┬─────────────┘
// │
-// ┌─────┴─────┐
-// │ FPRep<T> │
-// └───────────┘
+// ┌───────┴───────┐
+// │ FPRepImpl<T> │
+// └───────▲───────┘
// │
-// ┌─────┴─────┐
-// │ FPBits<T> │
-// └───────────┘
+// ┌────────┴────────┐
+// ┌─────┴─────┐ ┌─────┴─────┐
+// │ FPRep<T> │ │ FPBits<T> │
+// └───────────┘ └───────────┘
//
// - 'FPLayout' defines only a few constants, namely the 'StorageType' and
-// length of the sign, the exponent, fraction and significand parts.
+// length of the sign, the exponent, fraction and significand parts.
// - 'FPStorage' builds more constants on top of those from 'FPLayout' like
-// exponent bias and masks. It also holds the bit representation of the
-// floating point as a 'StorageType' type and defines tools to assemble or test
-// these parts.
+// exponent bias and masks. It also holds the bit representation of the
+// floating point as a 'StorageType' type and defines tools to assemble or
+// test these parts.
// - 'FPRepSem' defines functions to interact semantically with the floating
-// point representation. The default implementation is the one for 'IEEE754', a
-// specialization is provided for X86 Extended Precision.
-// - 'FPRep' derives from 'FPRepSem' and adds functions that are common to all
-// implementations.
-// - 'FPBits' exposes all functions from 'FPRep' but operates on the native C++
-// floating point type instead of 'FPType'.
+// point representation. The default implementation is the one for 'IEEE754',
+// a specialization is provided for X86 Extended Precision.
+// - 'FPRepImpl' derives from 'FPRepSem' and adds functions that are common to
+// all implementations or build on the ones in 'FPRepSem'.
+// - 'FPRep' exposes all functions from 'FPRepImpl' and returns 'FPRep'
+// instances when using Builders (static functions to create values).
+// - 'FPBits' exposes all the functions from 'FPRepImpl' but operates on the
+// native C++ floating point type instead of 'FPType'. An additional 'get_val'
+// function allows getting the C++ floating point type value back. Builders
+// called from 'FPBits' return 'FPBits' instances.
namespace internal {
@@ -197,12 +202,22 @@ template <FPType fp_type> struct FPStorage : public FPLayout<fp_type> {
static_assert((SIG_MASK | EXP_MASK | SIGN_MASK) == FP_MASK, "masks cover");
protected:
+ // 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);
+ }
+
// A stongly typed integer that prevents mixing and matching integers with
//
diff erent semantics.
template <typename T> struct TypedInt {
using value_type = T;
LIBC_INLINE constexpr explicit TypedInt(T value) : value(value) {}
LIBC_INLINE constexpr TypedInt(const TypedInt &value) = default;
+ LIBC_INLINE constexpr TypedInt &operator=(const TypedInt &value) = default;
LIBC_INLINE constexpr explicit operator T() const { return value; }
@@ -210,7 +225,14 @@ template <FPType fp_type> struct FPStorage : public FPLayout<fp_type> {
return StorageType(value);
}
- private:
+ LIBC_INLINE friend constexpr bool operator==(TypedInt a, TypedInt b) {
+ return a.value == b.value;
+ }
+ LIBC_INLINE friend constexpr bool operator!=(TypedInt a, TypedInt b) {
+ return a.value != b.value;
+ }
+
+ protected:
T value;
};
@@ -220,10 +242,13 @@ template <FPType fp_type> struct FPStorage : public FPLayout<fp_type> {
struct Exponent : public TypedInt<int32_t> {
using UP = TypedInt<int32_t>;
using UP::UP;
- LIBC_INLINE
- static constexpr auto MIN() { return Exponent(1 - EXP_BIAS); }
+ LIBC_INLINE static constexpr auto SUBNORMAL() {
+ return Exponent(-EXP_BIAS);
+ }
+ LIBC_INLINE static constexpr auto MIN() { return Exponent(1 - EXP_BIAS); }
LIBC_INLINE static constexpr auto ZERO() { return Exponent(0); }
LIBC_INLINE static constexpr auto MAX() { return Exponent(EXP_BIAS); }
+ LIBC_INLINE static constexpr auto INF() { return Exponent(EXP_BIAS + 1); }
};
// An opaque type to store a floating point biased exponent.
@@ -236,13 +261,10 @@ template <FPType fp_type> struct FPStorage : public FPLayout<fp_type> {
LIBC_INLINE constexpr BiasedExponent(Exponent exp)
: UP(static_cast<int32_t>(exp) + EXP_BIAS) {}
- // The exponent value for denormal numbers.
- LIBC_INLINE static constexpr auto BITS_ALL_ZEROES() {
- return BiasedExponent(uint32_t(0));
- }
- // The exponent value for infinity.
- LIBC_INLINE static constexpr auto BITS_ALL_ONES() {
- return BiasedExponent(uint32_t(2 * EXP_BIAS + 1));
+
+ // Cast operator to get convert from BiasedExponent to Exponent.
+ LIBC_INLINE constexpr operator Exponent() const {
+ return Exponent(UP::value - EXP_BIAS);
}
};
@@ -316,6 +338,23 @@ template <FPType fp_type> struct FPStorage : public FPLayout<fp_type> {
LIBC_INLINE constexpr StorageType exp_sig_bits() const {
return bits & EXP_SIG_MASK;
}
+
+ // Parts
+ LIBC_INLINE constexpr BiasedExponent biased_exponent() const {
+ return BiasedExponent(static_cast<uint32_t>(exp_bits() >> SIG_LEN));
+ }
+ LIBC_INLINE constexpr void set_biased_exponent(BiasedExponent biased) {
+ bits = merge(bits, encode(biased), EXP_MASK);
+ }
+
+public:
+ LIBC_INLINE constexpr Sign sign() const {
+ return (bits & SIGN_MASK) ? Sign::NEG : Sign::POS;
+ }
+ LIBC_INLINE constexpr void set_sign(Sign signVal) {
+ if (sign() != signVal)
+ bits ^= SIGN_MASK;
+ }
};
// This layer defines all functions that are specific to how the the floating
@@ -329,9 +368,8 @@ struct FPRepSem : public FPStorage<fp_type> {
using UP::FRACTION_MASK;
protected:
- using BiasedExp = typename UP::BiasedExponent;
- using Exp = typename UP::Exponent;
- using Sig = typename UP::Significand;
+ using typename UP::Exponent;
+ using typename UP::Significand;
using UP::encode;
using UP::exp_bits;
using UP::exp_sig_bits;
@@ -340,61 +378,66 @@ struct FPRepSem : public FPStorage<fp_type> {
public:
// Builders
+ LIBC_INLINE static constexpr RetT zero(Sign sign = Sign::POS) {
+ return RetT(encode(sign, Exponent::SUBNORMAL(), Significand::ZERO()));
+ }
LIBC_INLINE static constexpr RetT one(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exp::ZERO(), Sig::ZERO()));
+ return RetT(encode(sign, Exponent::ZERO(), Significand::ZERO()));
}
LIBC_INLINE static constexpr RetT min_subnormal(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ZEROES(), Sig::LSB()));
+ return RetT(encode(sign, Exponent::SUBNORMAL(), Significand::LSB()));
}
LIBC_INLINE static constexpr RetT max_subnormal(Sign sign = Sign::POS) {
return RetT(
- encode(sign, BiasedExp::BITS_ALL_ZEROES(), Sig::BITS_ALL_ONES()));
+ encode(sign, Exponent::SUBNORMAL(), Significand::BITS_ALL_ONES()));
}
LIBC_INLINE static constexpr RetT min_normal(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exp::MIN(), Sig::ZERO()));
+ return RetT(encode(sign, Exponent::MIN(), Significand::ZERO()));
}
LIBC_INLINE static constexpr RetT max_normal(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exp::MAX(), Sig::BITS_ALL_ONES()));
+ return RetT(encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES()));
}
LIBC_INLINE static constexpr RetT inf(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(), Sig::ZERO()));
+ return RetT(encode(sign, Exponent::INF(), Significand::ZERO()));
}
LIBC_INLINE static constexpr RetT signaling_nan(Sign sign = Sign::POS,
StorageType v = 0) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(),
- (v ? Sig(v) : (Sig::MSB() >> 1))));
+ return RetT(encode(sign, Exponent::INF(),
+ (v ? Significand(v) : (Significand::MSB() >> 1))));
}
LIBC_INLINE static constexpr RetT quiet_nan(Sign sign = Sign::POS,
StorageType v = 0) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(), Sig::MSB() | Sig(v)));
+ return RetT(
+ encode(sign, Exponent::INF(), Significand::MSB() | Significand(v)));
}
// Observers
+ LIBC_INLINE constexpr bool is_zero() const { return exp_sig_bits() == 0; }
LIBC_INLINE constexpr bool is_nan() const {
- return exp_sig_bits() > encode(BiasedExp::BITS_ALL_ONES(), Sig::ZERO());
+ return exp_sig_bits() > encode(Exponent::INF(), Significand::ZERO());
}
LIBC_INLINE constexpr bool is_quiet_nan() const {
- return exp_sig_bits() >= encode(BiasedExp::BITS_ALL_ONES(), Sig::MSB());
+ return exp_sig_bits() >= encode(Exponent::INF(), Significand::MSB());
}
LIBC_INLINE constexpr bool is_signaling_nan() const {
return is_nan() && !is_quiet_nan();
}
LIBC_INLINE constexpr bool is_inf() const {
- return exp_sig_bits() == encode(BiasedExp::BITS_ALL_ONES(), Sig::ZERO());
+ return exp_sig_bits() == encode(Exponent::INF(), Significand::ZERO());
}
LIBC_INLINE constexpr bool is_finite() const {
- return exp_bits() != encode(BiasedExp::BITS_ALL_ONES());
+ return exp_bits() != encode(Exponent::INF());
}
LIBC_INLINE
constexpr bool is_subnormal() const {
- return exp_bits() == encode(BiasedExp::BITS_ALL_ZEROES());
+ return exp_bits() == encode(Exponent::SUBNORMAL());
}
LIBC_INLINE constexpr bool is_normal() const {
return is_finite() && !is_subnormal();
}
// Returns the mantissa with the implicit bit set iff the current
// value is a valid normal number.
- LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
+ LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
if (is_subnormal())
return sig_bits();
return (StorageType(1) << UP::SIG_LEN) | sig_bits();
@@ -422,44 +465,50 @@ struct FPRepSem<FPType::X86_Binary80, RetT>
"whole significand");
protected:
- using BiasedExp = typename UP::BiasedExponent;
- using Sig = typename UP::Significand;
+ using typename UP::Exponent;
+ using typename UP::Significand;
using UP::encode;
using UP::UP;
public:
// Builders
+ LIBC_INLINE static constexpr RetT zero(Sign sign = Sign::POS) {
+ return RetT(encode(sign, Exponent::SUBNORMAL(), Significand::ZERO()));
+ }
LIBC_INLINE static constexpr RetT one(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exponent::ZERO(), Sig::MSB()));
+ return RetT(encode(sign, Exponent::ZERO(), Significand::MSB()));
}
LIBC_INLINE static constexpr RetT min_subnormal(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ZEROES(), Sig::LSB()));
+ return RetT(encode(sign, Exponent::SUBNORMAL(), Significand::LSB()));
}
LIBC_INLINE static constexpr RetT max_subnormal(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ZEROES(),
- Sig::BITS_ALL_ONES() ^ Sig::MSB()));
+ return RetT(encode(sign, Exponent::SUBNORMAL(),
+ Significand::BITS_ALL_ONES() ^ Significand::MSB()));
}
LIBC_INLINE static constexpr RetT min_normal(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exponent::MIN(), Sig::MSB()));
+ return RetT(encode(sign, Exponent::MIN(), Significand::MSB()));
}
LIBC_INLINE static constexpr RetT max_normal(Sign sign = Sign::POS) {
- return RetT(encode(sign, Exponent::MAX(), Sig::BITS_ALL_ONES()));
+ return RetT(encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES()));
}
LIBC_INLINE static constexpr RetT inf(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(), Sig::MSB()));
+ return RetT(encode(sign, Exponent::INF(), Significand::MSB()));
}
LIBC_INLINE static constexpr RetT signaling_nan(Sign sign = Sign::POS,
StorageType v = 0) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(),
- Sig::MSB() | (v ? Sig(v) : (Sig::MSB() >> 2))));
+ return RetT(encode(sign, Exponent::INF(),
+ Significand::MSB() |
+ (v ? Significand(v) : (Significand::MSB() >> 2))));
}
LIBC_INLINE static constexpr RetT quiet_nan(Sign sign = Sign::POS,
StorageType v = 0) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ONES(),
- Sig::MSB() | (Sig::MSB() >> 1) | Sig(v)));
+ return RetT(encode(sign, Exponent::INF(),
+ Significand::MSB() | (Significand::MSB() >> 1) |
+ Significand(v)));
}
// Observers
+ LIBC_INLINE constexpr bool is_zero() const { return exp_sig_bits() == 0; }
LIBC_INLINE constexpr bool is_nan() const {
// Most encoding forms from the table found in
// https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format
@@ -472,33 +521,33 @@ struct FPRepSem<FPType::X86_Binary80, RetT>
// - Quiet Not a Number
// - Unnormal
// This can be reduced to the following logic:
- if (exp_bits() == encode(BiasedExp::BITS_ALL_ONES()))
+ if (exp_bits() == encode(Exponent::INF()))
return !is_inf();
- if (exp_bits() != encode(BiasedExp::BITS_ALL_ZEROES()))
- return (sig_bits() & encode(Sig::MSB())) == 0;
+ if (exp_bits() != encode(Exponent::SUBNORMAL()))
+ return (sig_bits() & encode(Significand::MSB())) == 0;
return false;
}
LIBC_INLINE constexpr bool is_quiet_nan() const {
return exp_sig_bits() >=
- encode(BiasedExp::BITS_ALL_ONES(), Sig::MSB() | (Sig::MSB() >> 1));
+ encode(Exponent::INF(),
+ Significand::MSB() | (Significand::MSB() >> 1));
}
LIBC_INLINE constexpr bool is_signaling_nan() const {
return is_nan() && !is_quiet_nan();
}
LIBC_INLINE constexpr bool is_inf() const {
- return exp_sig_bits() == encode(BiasedExp::BITS_ALL_ONES(), Sig::MSB());
+ return exp_sig_bits() == encode(Exponent::INF(), Significand::MSB());
}
LIBC_INLINE constexpr bool is_finite() const {
return !is_inf() && !is_nan();
}
LIBC_INLINE
constexpr bool is_subnormal() const {
- return exp_bits() == encode(BiasedExp::BITS_ALL_ZEROES());
+ return exp_bits() == encode(Exponent::SUBNORMAL());
}
LIBC_INLINE constexpr bool is_normal() const {
const auto exp = exp_bits();
- if (exp == encode(BiasedExp::BITS_ALL_ZEROES()) ||
- exp == encode(BiasedExp::BITS_ALL_ONES()))
+ if (exp == encode(Exponent::SUBNORMAL()) || exp == encode(Exponent::INF()))
return false;
return get_implicit_bit();
}
@@ -520,21 +569,21 @@ struct FPRepSem<FPType::X86_Binary80, RetT>
}
};
-// 'FPRep' is the bottom of the class hierarchy that only deals with 'FPType'.
-// The operations dealing with specific float semantics are implemented by
-// 'FPRepSem' above and specialized when needed.
+// 'FPRepImpl' is the bottom of the class hierarchy that only deals with
+// 'FPType'. The operations dealing with specific float semantics are
+// implemented by 'FPRepSem' above and specialized when needed.
//
// The 'RetT' type is being propagated up to 'FPRepSem' so that the functions
// creating new values (Builders) can return the appropriate type. That is, when
// creating a value through 'FPBits' below the builder will return an 'FPBits'
-// value:
-// i.e., FPBits<float>::zero() // returns an FPBits<float>
-// When we don't care about specific C++ floating point type we can use 'FPRep'
-// directly and 'RetT' defaults to 'StorageType':
-// i.e., FPRep<FPType:IEEE754_Binary32:>::zero() // returns an 'uint32_t'
-template <FPType fp_type,
- typename RetT = typename FPLayout<fp_type>::StorageType>
-struct FPRep : public FPRepSem<fp_type, RetT> {
+// value.
+// FPBits<float>::zero(); // returns an FPBits<>
+//
+// When we don't care about specific C++ floating point type we can use
+// 'FPRep' and specify the 'FPType' directly.
+// FPRep<FPType::IEEE754_Binary32:>::zero() // returns an FPRep<>
+template <FPType fp_type, typename RetT>
+struct FPRepImpl : public FPRepSem<fp_type, RetT> {
using UP = FPRepSem<fp_type, RetT>;
using StorageType = typename UP::StorageType;
@@ -544,8 +593,9 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
using UP::exp_bits;
using UP::exp_sig_bits;
- using BiasedExp = typename UP::BiasedExponent;
- using Sig = typename UP::Significand;
+ using typename UP::BiasedExponent;
+ using typename UP::Exponent;
+ using typename UP::Significand;
using UP::FP_MASK;
using UP::SIG_LEN;
@@ -559,14 +609,15 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
LIBC_INLINE_VAR static constexpr int MAX_BIASED_EXPONENT =
(1 << UP::EXP_LEN) - 1;
- LIBC_INLINE constexpr FPRep() = default;
- LIBC_INLINE constexpr explicit FPRep(StorageType x) : UP(x) {}
+ // CTors
+ LIBC_INLINE constexpr FPRepImpl() = default;
+ LIBC_INLINE constexpr explicit FPRepImpl(StorageType x) : UP(x) {}
// Comparison
- LIBC_INLINE constexpr friend bool operator==(FPRep a, FPRep b) {
+ LIBC_INLINE constexpr friend bool operator==(FPRepImpl a, FPRepImpl b) {
return a.uintval() == b.uintval();
}
- LIBC_INLINE constexpr friend bool operator!=(FPRep a, FPRep b) {
+ LIBC_INLINE constexpr friend bool operator!=(FPRepImpl a, FPRepImpl b) {
return a.uintval() != b.uintval();
}
@@ -577,9 +628,6 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
}
// Builders
- LIBC_INLINE static constexpr RetT zero(Sign sign = Sign::POS) {
- return RetT(encode(sign, BiasedExp::BITS_ALL_ZEROES(), Sig::ZERO()));
- }
using UP::inf;
using UP::max_normal;
using UP::max_subnormal;
@@ -588,6 +636,7 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
using UP::one;
using UP::quiet_nan;
using UP::signaling_nan;
+ using UP::zero;
// Modifiers
LIBC_INLINE constexpr RetT abs() const {
@@ -596,8 +645,6 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
// Observers
using UP::get_explicit_mantissa;
- LIBC_INLINE constexpr bool is_zero() const { return exp_sig_bits() == 0; }
- LIBC_INLINE constexpr bool is_inf_or_nan() const { return !is_finite(); }
using UP::is_finite;
using UP::is_inf;
using UP::is_nan;
@@ -605,29 +652,22 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
using UP::is_quiet_nan;
using UP::is_signaling_nan;
using UP::is_subnormal;
+ using UP::is_zero;
+ using UP::sign;
+ LIBC_INLINE constexpr bool is_inf_or_nan() const { return !is_finite(); }
LIBC_INLINE constexpr bool is_neg() const { return sign().is_neg(); }
LIBC_INLINE constexpr bool is_pos() const { return sign().is_pos(); }
- // Parts
- LIBC_INLINE constexpr Sign sign() const {
- return (bits & SIGN_MASK) ? Sign::NEG : Sign::POS;
- }
-
- LIBC_INLINE constexpr void set_sign(Sign signVal) {
- if (sign() != signVal)
- bits ^= SIGN_MASK;
- }
-
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
- return uint16_t((bits & UP::EXP_MASK) >> UP::SIG_LEN);
+ return static_cast<uint16_t>(static_cast<uint32_t>(UP::biased_exponent()));
}
LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
- bits = merge(bits, biased << SIG_LEN, EXP_MASK);
+ UP::set_biased_exponent(BiasedExponent((int32_t)biased));
}
LIBC_INLINE constexpr int get_exponent() const {
- return int(get_biased_exponent()) - EXP_BIAS;
+ return static_cast<int32_t>(Exponent(UP::biased_exponent()));
}
// If the number is subnormal, the exponent is treated as if it were the
@@ -637,14 +677,12 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
// will give a slightly incorrect result. Additionally, zero has an exponent
// of zero, and that should actually be treated as zero.
LIBC_INLINE constexpr int get_explicit_exponent() const {
- const int biased_exp = int(get_biased_exponent());
- if (is_zero()) {
- return 0;
- } else if (biased_exp == 0) {
- return 1 - EXP_BIAS;
- } else {
- return biased_exp - EXP_BIAS;
- }
+ Exponent exponent(UP::biased_exponent());
+ if (is_zero())
+ exponent = Exponent::ZERO();
+ if (exponent == Exponent::SUBNORMAL())
+ exponent = Exponent::MIN();
+ return static_cast<int32_t>(exponent);
}
LIBC_INLINE constexpr StorageType get_mantissa() const {
@@ -652,7 +690,7 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
}
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
- bits = merge(bits, mantVal, FRACTION_MASK);
+ bits = UP::merge(bits, mantVal, FRACTION_MASK);
}
// Unsafe function to create a floating point representation.
@@ -663,12 +701,12 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
create_value(Sign sign, StorageType biased_exp, StorageType mantissa) {
static_assert(fp_type != FPType::X86_Binary80,
"This function is not tested for X86 Extended Precision");
- return RetT(encode(sign, BiasedExp(static_cast<uint32_t>(biased_exp)),
- Sig(mantissa)));
+ return RetT(encode(sign, BiasedExponent(static_cast<uint32_t>(biased_exp)),
+ Significand(mantissa)));
}
- // The function converts integer number and unbiased exponent to proper float
- // T type:
+ // The function converts integer number and unbiased exponent to proper
+ // float T type:
// Result = number * 2^(ep+1 - exponent_bias)
// Be careful!
// 1) "ep" is the raw exponent value.
@@ -680,7 +718,7 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
LIBC_INLINE static constexpr RetT make_value(StorageType number, int ep) {
static_assert(fp_type != FPType::X86_Binary80,
"This function is not tested for X86 Extended Precision");
- FPRep result;
+ FPRepImpl result;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - UP::EXP_LEN;
number <<= lz;
@@ -695,15 +733,18 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
}
return RetT(result.uintval());
}
+};
-private:
- // 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);
+// A generic class to manipulate floating point formats.
+// It derives its functionality to FPRepImpl above.
+template <FPType fp_type>
+struct FPRep : public FPRepImpl<fp_type, FPRep<fp_type>> {
+ using UP = FPRepImpl<fp_type, FPRep<fp_type>>;
+ using StorageType = typename UP::StorageType;
+ using UP::UP;
+
+ LIBC_INLINE constexpr explicit operator StorageType() const {
+ return UP::uintval();
}
};
@@ -741,12 +782,12 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
}
// A generic class to manipulate C++ floating point formats.
-// It derives most of its functionality to FPRep above.
+// It derives its functionality to FPRepImpl above.
template <typename T>
-struct FPBits final : public internal::FPRep<get_fp_type<T>(), FPBits<T>> {
+struct FPBits final : public internal::FPRepImpl<get_fp_type<T>(), FPBits<T>> {
static_assert(cpp::is_floating_point_v<T>,
"FPBits instantiated with invalid type.");
- using UP = internal::FPRep<get_fp_type<T>(), FPBits<T>>;
+ using UP = internal::FPRepImpl<get_fp_type<T>(), FPBits<T>>;
using StorageType = typename UP::StorageType;
// Constructors.
diff --git a/libc/test/src/__support/FPUtil/fpbits_test.cpp b/libc/test/src/__support/FPUtil/fpbits_test.cpp
index 17737af9ce149..65823511e82f5 100644
--- a/libc/test/src/__support/FPUtil/fpbits_test.cpp
+++ b/libc/test/src/__support/FPUtil/fpbits_test.cpp
@@ -231,47 +231,47 @@ enum class FP {
QUIET_NAN
};
+constexpr FP all_fp_values[] = {
+ FP::ZERO, FP::MIN_SUBNORMAL, FP::MAX_SUBNORMAL,
+ FP::MIN_NORMAL, FP::ONE, FP::MAX_NORMAL,
+ FP::INF, FP::SIGNALING_NAN, FP::QUIET_NAN,
+};
+
+constexpr Sign all_signs[] = {Sign::POS, Sign::NEG};
+
using FPTypes = LIBC_NAMESPACE::testing::TypeList<
FPRep<FPType::IEEE754_Binary16>, FPRep<FPType::IEEE754_Binary32>,
FPRep<FPType::IEEE754_Binary64>, FPRep<FPType::IEEE754_Binary128>,
FPRep<FPType::X86_Binary80>>;
+template <typename T> constexpr auto make(Sign sign, FP fp) {
+ switch (fp) {
+ case FP::ZERO:
+ return T::zero(sign);
+ case FP::MIN_SUBNORMAL:
+ return T::min_subnormal(sign);
+ case FP::MAX_SUBNORMAL:
+ return T::max_subnormal(sign);
+ case FP::MIN_NORMAL:
+ return T::min_normal(sign);
+ case FP::ONE:
+ return T::one(sign);
+ case FP::MAX_NORMAL:
+ return T::max_normal(sign);
+ case FP::INF:
+ return T::inf(sign);
+ case FP::SIGNALING_NAN:
+ return T::signaling_nan(sign);
+ case FP::QUIET_NAN:
+ return T::quiet_nan(sign);
+ }
+};
+
// Tests all properties for all types of float.
TYPED_TEST(LlvmLibcFPBitsTest, Properties, FPTypes) {
- static constexpr auto make_storage = [](Sign sign, FP fp) {
- switch (fp) {
- case FP::ZERO:
- return T::zero(sign);
- case FP::MIN_SUBNORMAL:
- return T::min_subnormal(sign);
- case FP::MAX_SUBNORMAL:
- return T::max_subnormal(sign);
- case FP::MIN_NORMAL:
- return T::min_normal(sign);
- case FP::ONE:
- return T::one(sign);
- case FP::MAX_NORMAL:
- return T::max_normal(sign);
- case FP::INF:
- return T::inf(sign);
- case FP::SIGNALING_NAN:
- return T::signaling_nan(sign);
- case FP::QUIET_NAN:
- return T::quiet_nan(sign);
- }
- };
- static constexpr auto make = [](Sign sign, FP fp) -> T {
- return T(make_storage(sign, fp));
- };
- constexpr FP fp_values[] = {
- FP::ZERO, FP::MIN_SUBNORMAL, FP::MAX_SUBNORMAL,
- FP::MIN_NORMAL, FP::ONE, FP::MAX_NORMAL,
- FP::INF, FP::SIGNALING_NAN, FP::QUIET_NAN,
- };
- constexpr Sign signs[] = {Sign::POS, Sign::NEG};
- for (Sign sign : signs) {
- for (FP fp : fp_values) {
- const T value = make(sign, fp);
+ for (Sign sign : all_signs) {
+ for (FP fp : all_fp_values) {
+ const T value = make<T>(sign, fp);
// is_zero
ASSERT_EQ(value.is_zero(), fp == FP::ZERO);
// is_inf_or_nan
More information about the libc-commits
mailing list