Skip to content

[libc][NFC] Move functions from FPBits to FPRep, make bits member private #79974

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 49 additions & 51 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -551,10 +551,13 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
using UP::SIG_LEN;

public:
// Constants.
using UP::EXP_BIAS;
using UP::EXP_MASK;
using UP::FRACTION_MASK;
using UP::SIGN_MASK;
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) {}
Expand Down Expand Up @@ -652,6 +655,47 @@ struct FPRep : public FPRepSem<fp_type, RetT> {
bits = merge(bits, mantVal, FRACTION_MASK);
}

// Unsafe function to create a floating point representation.
// It simply packs the sign, biased exponent and mantissa values without
// checking bound nor normalization.
// FIXME: Use an uint32_t for 'biased_exp'.
LIBC_INLINE static constexpr 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)));
}

// 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.
// 2) The function adds +1 to ep for seamless normalized to denormalized
// transition.
// 3) The function does not check exponent high limit.
// 4) "number" zero value is not processed correctly.
// 5) Number is unsigned, so the result can be only positive.
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;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - UP::EXP_LEN;
number <<= lz;
ep -= lz;

if (LIBC_LIKELY(ep >= 0)) {
// Implicit number bit will be removed by mask
result.set_mantissa(number);
result.set_biased_exponent(ep + 1);
} else {
result.set_mantissa(number >> -ep);
}
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
Expand Down Expand Up @@ -696,79 +740,33 @@ template <typename T> LIBC_INLINE static constexpr FPType get_fp_type() {
static_assert(cpp::always_false<UnqualT>, "Unsupported type");
}

// A generic class to manipulate floating point formats.
// A generic class to manipulate C++ floating point formats.
// It derives most of its functionality to FPRep above.
template <typename T>
struct FPBits final : public internal::FPRep<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 Rep = UP;
using StorageType = typename UP::StorageType;

using UP::bits;

// Constants.
LIBC_INLINE_VAR static constexpr int MAX_BIASED_EXPONENT =
(1 << UP::EXP_LEN) - 1;

// 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);
UP::bits = cpp::bit_cast<StorageType>(x);
} else if constexpr (cpp::is_same_v<Unqual, StorageType>) {
bits = x;
UP::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); }

// TODO: Use an uint32_t for 'biased_exp'.
LIBC_INLINE static constexpr FPBits<T>
create_value(Sign sign, StorageType biased_exp, StorageType mantissa) {
static_assert(get_fp_type<T>() != FPType::X86_Binary80,
"This function is not tested for X86 Extended Precision");
return FPBits(UP::encode(
sign, typename UP::BiasedExponent(static_cast<uint32_t>(biased_exp)),
typename UP::Significand(mantissa)));
}

// The function convert integer number and unbiased exponent to proper float
// T type:
// Result = number * 2^(ep+1 - exponent_bias)
// Be careful!
// 1) "ep" is raw exponent value.
// 2) The function add to +1 to ep for seamless normalized to denormalized
// transition.
// 3) The function did not check exponent high limit.
// 4) "number" zero value is not processed correctly.
// 5) Number is unsigned, so the result can be only positive.
LIBC_INLINE static constexpr FPBits<T> make_value(StorageType number,
int ep) {
static_assert(get_fp_type<T>() != FPType::X86_Binary80,
"This function is not tested for X86 Extended Precision");
FPBits<T> result;
// offset: +1 for sign, but -1 for implicit first bit
int lz = cpp::countl_zero(number) - UP::EXP_LEN;
number <<= lz;
ep -= lz;

if (LIBC_LIKELY(ep >= 0)) {
// Implicit number bit will be removed by mask
result.set_mantissa(number);
result.set_biased_exponent(ep + 1);
} else {
result.set_mantissa(number >> -ep);
}
return result;
}
// Floating-point conversions.
LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(UP::bits); }
};

} // namespace fputil
Expand Down
4 changes: 2 additions & 2 deletions libc/src/math/generic/explogxf.h
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ LIBC_INLINE static double log2_eval(double x) {
int p1 = (bs.get_mantissa() >> (FPB::FRACTION_LEN - LOG_P1_BITS)) &
(LOG_P1_SIZE - 1);

bs.bits &= FPB::FRACTION_MASK >> LOG_P1_BITS;
bs.set_uintval(bs.uintval() & (FPB::FRACTION_MASK >> LOG_P1_BITS));
bs.set_biased_exponent(FPB::EXP_BIAS);
double dx = (bs.get_val() - 1.0) * LOG_P1_1_OVER[p1];

Expand Down Expand Up @@ -313,7 +313,7 @@ LIBC_INLINE static double log_eval(double x) {
int p1 = static_cast<int>(bs.get_mantissa() >> (FPB::FRACTION_LEN - 7));

// Set bs to (1 + (mx - p1*2^(-7))
bs.bits &= FPB::FRACTION_MASK >> 7;
bs.set_uintval(bs.uintval() & (FPB::FRACTION_MASK >> 7));
bs.set_biased_exponent(FPB::EXP_BIAS);
// dx = (mx - p1*2^(-7)) / (1 + p1*2^(-7)).
double dx = (bs.get_val() - 1.0) * ONE_OVER_F[p1];
Expand Down
4 changes: 2 additions & 2 deletions libc/src/math/generic/hypotf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@ LLVM_LIBC_FUNCTION(float, hypotf, (float x, float y)) {
uint64_t lrs = result.uintval() & mask;

if (lrs == 0x0000'0000'1000'0000ULL && err < diff) {
result.bits |= 1ULL;
result.set_uintval(result.uintval() | 1ULL);
} else if (lrs == 0x0000'0000'3000'0000ULL && err > diff) {
result.bits -= 1ULL;
result.set_uintval(result.uintval() - 1ULL);
}
} else {
FPBits bits_x(x), bits_y(y);
Expand Down
2 changes: 1 addition & 1 deletion libc/src/math/generic/log1pf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ LIBC_INLINE float log(double x) {
FPBits f = xbits;

// Clear the lowest 45 bits.
f.bits &= ~0x0000'1FFF'FFFF'FFFFULL;
f.set_uintval(f.uintval() & ~0x0000'1FFF'FFFF'FFFFULL);

double d = xbits.get_val() - f.get_val();
d *= ONE_OVER_F[f_index];
Expand Down
6 changes: 4 additions & 2 deletions libc/src/math/generic/range_reduction_fma.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@ LIBC_INLINE int64_t large_range_reduction(double x, int x_exp, double &y) {
// - When |x| >= 2^55, the LSB of double(x * THIRTYTWO_OVER_PI[0]) is at
// least 2^6.
fputil::FPBits<double> prod_hi(x * THIRTYTWO_OVER_PI[0]);
prod_hi.bits &= (x_exp < 55) ? (~0xfffULL) : (~0ULL); // |x| < 2^55
prod_hi.set_uintval(prod_hi.uintval() &
((x_exp < 55) ? (~0xfffULL) : (~0ULL))); // |x| < 2^55
double k_hi = fputil::nearest_integer(prod_hi.get_val());
double truncated_prod = fputil::fma(x, THIRTYTWO_OVER_PI[0], -k_hi);
double prod_lo = fputil::fma(x, THIRTYTWO_OVER_PI[1], truncated_prod);
Expand All @@ -70,7 +71,8 @@ LIBC_INLINE int64_t large_range_reduction(double x, int x_exp, double &y) {
// - When |x| >= 2^110, the LSB of double(x * THIRTYTWO_OVER_PI[1]) is at
// least 64.
fputil::FPBits<double> prod_hi(x * THIRTYTWO_OVER_PI[1]);
prod_hi.bits &= (x_exp < 110) ? (~0xfffULL) : (~0ULL); // |x| < 2^110
prod_hi.set_uintval(prod_hi.uintval() &
((x_exp < 110) ? (~0xfffULL) : (~0ULL))); // |x| < 2^110
double k_hi = fputil::nearest_integer(prod_hi.get_val());
double truncated_prod = fputil::fma(x, THIRTYTWO_OVER_PI[1], -k_hi);
double prod_lo = fputil::fma(x, THIRTYTWO_OVER_PI[2], truncated_prod);
Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/math/atanhf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ TEST_F(LlvmLibcAtanhfTest, SpecialNumbers) {
EXPECT_MATH_ERRNO(ERANGE);

auto bt = FPBits(1.0f);
bt.bits += 1;
bt.set_uintval(bt.uintval() + 1);

LIBC_NAMESPACE::fputil::clear_except(FE_ALL_EXCEPT);
EXPECT_FP_EQ_ALL_ROUNDING(aNaN, LIBC_NAMESPACE::atanhf(bt.get_val()));
Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/math/smoke/atanhf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ TEST_F(LlvmLibcAtanhfTest, SpecialNumbers) {
EXPECT_MATH_ERRNO(ERANGE);

auto bt = FPBits(1.0f);
bt.bits += 1;
bt.set_uintval(bt.uintval() + 1);

EXPECT_FP_IS_NAN_WITH_EXCEPTION(LIBC_NAMESPACE::atanhf(bt.get_val()),
FE_INVALID);
Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/math/smoke/nan_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class LlvmLibcNanTest : public LIBC_NAMESPACE::testing::Test {
double result = LIBC_NAMESPACE::nan(input_str);
auto actual_fp = LIBC_NAMESPACE::fputil::FPBits<double>(result);
auto expected_fp = LIBC_NAMESPACE::fputil::FPBits<double>(bits);
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
EXPECT_EQ(actual_fp.uintval(), expected_fp.uintval());
};
};

Expand Down
2 changes: 1 addition & 1 deletion libc/test/src/math/smoke/nanf_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class LlvmLibcNanfTest : public LIBC_NAMESPACE::testing::Test {
float result = LIBC_NAMESPACE::nanf(input_str);
auto actual_fp = LIBC_NAMESPACE::fputil::FPBits<float>(result);
auto expected_fp = LIBC_NAMESPACE::fputil::FPBits<float>(bits);
EXPECT_EQ(actual_fp.bits, expected_fp.bits);
EXPECT_EQ(actual_fp.uintval(), expected_fp.uintval());
};
};

Expand Down