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 │ -// └───────────┘ +// ┌───────┴───────┐ +// │ FPRepImpl │ +// └───────▲───────┘ // │ -// ┌─────┴─────┐ -// │ FPBits │ -// └───────────┘ +// ┌────────┴────────┐ +// ┌─────┴─────┐ ┌─────┴─────┐ +// │ FPRep │ │ FPBits │ +// └───────────┘ └───────────┘ // // - '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 struct FPStorage : public FPLayout { 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 // different semantics. template 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 struct FPStorage : public FPLayout { 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 struct FPStorage : public FPLayout { struct Exponent : public TypedInt { using UP = TypedInt; 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 struct FPStorage : public FPLayout { LIBC_INLINE constexpr BiasedExponent(Exponent exp) : UP(static_cast(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 struct FPStorage : public FPLayout { 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(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 { 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 { 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 "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 // - 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 } }; -// '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::zero() // returns an FPBits -// When we don't care about specific C++ floating point type we can use 'FPRep' -// directly and 'RetT' defaults to 'StorageType': -// i.e., FPRep::zero() // returns an 'uint32_t' -template ::StorageType> -struct FPRep : public FPRepSem { +// value. +// FPBits::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::zero() // returns an FPRep<> +template +struct FPRepImpl : public FPRepSem { using UP = FPRepSem; using StorageType = typename UP::StorageType; @@ -544,8 +593,9 @@ struct FPRep : public FPRepSem { 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 { 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 { } // 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 { 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 { // 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 { 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(static_cast(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(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 { // 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(exponent); } LIBC_INLINE constexpr StorageType get_mantissa() const { @@ -652,7 +690,7 @@ struct FPRep : public FPRepSem { } 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 { 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(biased_exp)), - Sig(mantissa))); + return RetT(encode(sign, BiasedExponent(static_cast(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 { 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 { } 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 +struct FPRep : public FPRepImpl> { + using UP = FPRepImpl>; + using StorageType = typename UP::StorageType; + using UP::UP; + + LIBC_INLINE constexpr explicit operator StorageType() const { + return UP::uintval(); } }; @@ -741,12 +782,12 @@ template 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 -struct FPBits final : public internal::FPRep(), FPBits> { +struct FPBits final : public internal::FPRepImpl(), FPBits> { static_assert(cpp::is_floating_point_v, "FPBits instantiated with invalid type."); - using UP = internal::FPRep(), FPBits>; + using UP = internal::FPRepImpl(), FPBits>; 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, FPRep, FPRep, FPRep, FPRep>; +template 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(sign, fp); // is_zero ASSERT_EQ(value.is_zero(), fp == FP::ZERO); // is_inf_or_nan