Skip to content

[libc][NFC] Introduce a Sign type for FPBits #78500

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 10 commits into from
Jan 18, 2024
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
12 changes: 6 additions & 6 deletions libc/src/__support/FPUtil/BasicOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,23 @@ namespace fputil {
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T abs(T x) {
FPBits<T> bits(x);
bits.set_sign(0);
bits.set_sign(Sign::POS);
return T(bits);
}

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T fmin(T x, T y) {
FPBits<T> bitx(x), bity(y);
const FPBits<T> bitx(x), bity(y);

if (bitx.is_nan()) {
return y;
} else if (bity.is_nan()) {
return x;
} else if (bitx.get_sign() != bity.get_sign()) {
} else if (bitx.sign() != bity.sign()) {
// To make sure that fmin(+0, -0) == -0 == fmin(-0, +0), whenever x and
// y has different signs and both are not NaNs, we return the number
// with negative sign.
return (bitx.get_sign() ? x : y);
return (bitx.is_neg()) ? x : y;
} else {
return (x < y ? x : y);
}
Expand All @@ -50,11 +50,11 @@ LIBC_INLINE T fmax(T x, T y) {
return y;
} else if (bity.is_nan()) {
return x;
} else if (bitx.get_sign() != bity.get_sign()) {
} else if (bitx.sign() != bity.sign()) {
// To make sure that fmax(+0, -0) == +0 == fmax(-0, +0), whenever x and
// y has different signs and both are not NaNs, we return the number
// with positive sign.
return (bitx.get_sign() ? y : x);
return (bitx.is_neg() ? y : x);
} else {
return (x > y ? x : y);
}
Expand Down
11 changes: 6 additions & 5 deletions libc/src/__support/FPUtil/DivisionAndRemainderOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,14 @@ LIBC_INLINE T remquo(T x, T y, int &q) {
return x;
}

bool result_sign = (xbits.get_sign() == ybits.get_sign() ? false : true);
const Sign result_sign =
(xbits.sign() == ybits.sign() ? Sign::POS : Sign::NEG);

// Once we know the sign of the result, we can just operate on the absolute
// values. The correct sign can be applied to the result after the result
// is evaluated.
xbits.set_sign(0);
ybits.set_sign(0);
xbits.set_sign(Sign::POS);
ybits.set_sign(Sign::POS);

NormalFloat<T> normalx(xbits), normaly(ybits);
int exp = normalx.exponent - normaly.exponent;
Expand All @@ -72,7 +73,7 @@ LIBC_INLINE T remquo(T x, T y, int &q) {

mx = n - my;
if (mx == 0) {
q = result_sign ? -q : q;
q = result_sign.is_neg() ? -q : q;
return LIBC_NAMESPACE::fputil::copysign(T(0.0), x);
}
}
Expand Down Expand Up @@ -107,7 +108,7 @@ LIBC_INLINE T remquo(T x, T y, int &q) {
native_remainder = -native_remainder;
}

q = result_sign ? -q : q;
q = result_sign.is_neg() ? -q : q;
if (native_remainder == T(0.0))
return LIBC_NAMESPACE::fputil::copysign(T(0.0), x);
return native_remainder;
Expand Down
122 changes: 78 additions & 44 deletions libc/src/__support/FPUtil/FPBits.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,32 @@ enum class FPType {
X86_Binary80,
};

// A type to interact with floating point type signs.
// This may be moved outside of 'fputil' if useful.
struct Sign {
LIBC_INLINE constexpr bool is_pos() const { return !is_negative; }
LIBC_INLINE constexpr bool is_neg() const { return is_negative; }

LIBC_INLINE friend constexpr bool operator==(Sign a, Sign b) {
return a.is_negative == b.is_negative;
}
LIBC_INLINE friend constexpr bool operator!=(Sign a, Sign b) {
return !(a == b);
}

static const Sign POS;
static const Sign NEG;

private:
LIBC_INLINE constexpr explicit Sign(bool is_negative)
: is_negative(is_negative) {}

bool is_negative;
};

LIBC_INLINE_VAR constexpr Sign Sign::NEG = Sign(true);
LIBC_INLINE_VAR constexpr Sign Sign::POS = Sign(false);

// The classes hierarchy is as follows:
//
// ┌───────────────────┐
Expand Down Expand Up @@ -273,9 +299,9 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
return encode(exp) | encode(sig);
}

LIBC_INLINE static constexpr StorageType encode(bool sign, BiasedExponent exp,
LIBC_INLINE static constexpr StorageType encode(Sign sign, BiasedExponent exp,
Significand sig) {
if (sign)
if (sign.is_neg())
return SIGN_MASK | encode(exp, sig);
return encode(exp, sig);
}
Expand Down Expand Up @@ -309,12 +335,12 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
StorageType bits = 0;

public:
LIBC_INLINE constexpr bool get_sign() const {
return (bits & SIGN_MASK) != 0;
LIBC_INLINE constexpr Sign sign() const {
return (bits & SIGN_MASK) ? Sign::NEG : Sign::POS;
}

LIBC_INLINE constexpr void set_sign(bool signVal) {
if (get_sign() != signVal)
LIBC_INLINE constexpr void set_sign(Sign signVal) {
if (sign() != signVal)
bits ^= SIGN_MASK;
}

Expand Down Expand Up @@ -363,6 +389,9 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
LIBC_INLINE constexpr bool is_zero() const {
return (bits & EXP_SIG_MASK) == 0;
}

LIBC_INLINE constexpr bool is_neg() const { return sign().is_neg(); }
LIBC_INLINE constexpr bool is_pos() const { return sign().is_pos(); }
};

namespace internal {
Expand Down Expand Up @@ -421,35 +450,37 @@ template <FPType fp_type> struct FPRep : public FPRepBase<fp_type> {
return is_finite() && !is_subnormal();
}

LIBC_INLINE static constexpr StorageType zero(bool sign = false) {
LIBC_INLINE static constexpr StorageType zero(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType one(bool sign = false) {
LIBC_INLINE static constexpr StorageType one(Sign sign = Sign::POS) {
return encode(sign, Exponent::ZERO(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
LIBC_INLINE static constexpr StorageType
min_subnormal(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(), Significand::LSB());
}
LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
LIBC_INLINE static constexpr StorageType
max_subnormal(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(),
Significand::BITS_ALL_ONES());
}
LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
LIBC_INLINE static constexpr StorageType min_normal(Sign sign = Sign::POS) {
return encode(sign, Exponent::MIN(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
LIBC_INLINE static constexpr StorageType max_normal(Sign sign = Sign::POS) {
return encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES());
}
LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
LIBC_INLINE static constexpr StorageType inf(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
LIBC_INLINE static constexpr StorageType build_nan(Sign sign = Sign::POS,
StorageType v = 0) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(),
(v ? Significand(v) : (Significand::MSB() >> 1)));
}
LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
StorageType v = 0) {
LIBC_INLINE static constexpr StorageType
build_quiet_nan(Sign sign = Sign::POS, StorageType v = 0) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(),
Significand::MSB() | Significand(v));
}
Expand Down Expand Up @@ -539,36 +570,38 @@ struct FPRep<FPType::X86_Binary80> : public FPRepBase<FPType::X86_Binary80> {
return get_implicit_bit();
}

LIBC_INLINE static constexpr StorageType zero(bool sign = false) {
LIBC_INLINE static constexpr StorageType zero(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(), Significand::ZERO());
}
LIBC_INLINE static constexpr StorageType one(bool sign = false) {
LIBC_INLINE static constexpr StorageType one(Sign sign = Sign::POS) {
return encode(sign, Exponent::ZERO(), Significand::MSB());
}
LIBC_INLINE static constexpr StorageType min_subnormal(bool sign = false) {
LIBC_INLINE static constexpr StorageType
min_subnormal(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(), Significand::LSB());
}
LIBC_INLINE static constexpr StorageType max_subnormal(bool sign = false) {
LIBC_INLINE static constexpr StorageType
max_subnormal(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ZEROES(),
Significand::BITS_ALL_ONES() ^ Significand::MSB());
}
LIBC_INLINE static constexpr StorageType min_normal(bool sign = false) {
LIBC_INLINE static constexpr StorageType min_normal(Sign sign = Sign::POS) {
return encode(sign, Exponent::MIN(), Significand::MSB());
}
LIBC_INLINE static constexpr StorageType max_normal(bool sign = false) {
LIBC_INLINE static constexpr StorageType max_normal(Sign sign = Sign::POS) {
return encode(sign, Exponent::MAX(), Significand::BITS_ALL_ONES());
}
LIBC_INLINE static constexpr StorageType inf(bool sign = false) {
LIBC_INLINE static constexpr StorageType inf(Sign sign = Sign::POS) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(), Significand::MSB());
}
LIBC_INLINE static constexpr StorageType build_nan(bool sign = false,
LIBC_INLINE static constexpr StorageType build_nan(Sign sign = Sign::POS,
StorageType v = 0) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(),
Significand::MSB() |
(v ? Significand(v) : (Significand::MSB() >> 2)));
}
LIBC_INLINE static constexpr StorageType build_quiet_nan(bool sign = false,
StorageType v = 0) {
LIBC_INLINE static constexpr StorageType
build_quiet_nan(Sign sign = Sign::POS, StorageType v = 0) {
return encode(sign, BiasedExponent::BITS_ALL_ONES(),
Significand::MSB() | (Significand::MSB() >> 1) |
Significand(v));
Expand Down Expand Up @@ -642,10 +675,10 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {

// Constants.
static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
static constexpr StorageType MIN_NORMAL = UP::min_normal(false);
static constexpr StorageType MAX_NORMAL = UP::max_normal(false);
static constexpr StorageType MIN_SUBNORMAL = UP::min_subnormal(false);
static constexpr StorageType MAX_SUBNORMAL = UP::max_subnormal(false);
static constexpr StorageType MIN_NORMAL = UP::min_normal(Sign::POS);
static constexpr StorageType MAX_NORMAL = UP::max_normal(Sign::POS);
static constexpr StorageType MIN_SUBNORMAL = UP::min_subnormal(Sign::POS);
static constexpr StorageType MAX_SUBNORMAL = UP::max_subnormal(Sign::POS);

// Constructors.
LIBC_INLINE constexpr FPBits() = default;
Expand Down Expand Up @@ -675,45 +708,46 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {

// Methods below this are used by tests.

LIBC_INLINE static constexpr T zero(bool sign = false) {
return FPBits(UP::zero(sign)).get_val();
LIBC_INLINE static constexpr T one(Sign sign = Sign::POS) {
return FPBits(UP::one(sign)).get_val();
}

LIBC_INLINE static constexpr T neg_zero() { return zero(true); }
LIBC_INLINE static constexpr T zero(Sign sign = Sign::POS) {
return FPBits(UP::zero(sign)).get_val();
}

LIBC_INLINE static constexpr T inf(bool sign = false) {
LIBC_INLINE static constexpr T inf(Sign sign = Sign::POS) {
return FPBits(UP::inf(sign)).get_val();
}

LIBC_INLINE static constexpr T neg_inf() { return inf(true); }

LIBC_INLINE static constexpr T min_normal() {
return FPBits(UP::min_normal(false)).get_val();
return FPBits(UP::min_normal(Sign::POS)).get_val();
}

LIBC_INLINE static constexpr T max_normal() {
return FPBits(UP::max_normal(false)).get_val();
return FPBits(UP::max_normal(Sign::POS)).get_val();
}

LIBC_INLINE static constexpr T min_denormal() {
return FPBits(UP::min_subnormal(false)).get_val();
return FPBits(UP::min_subnormal(Sign::POS)).get_val();
}

LIBC_INLINE static constexpr T max_denormal() {
return FPBits(UP::max_subnormal(false)).get_val();
return FPBits(UP::max_subnormal(Sign::POS)).get_val();
}

LIBC_INLINE static constexpr T build_nan(StorageType v) {
return FPBits(UP::build_nan(false, v)).get_val();
return FPBits(UP::build_nan(Sign::POS, v)).get_val();
}

LIBC_INLINE static constexpr T build_quiet_nan(StorageType v) {
return FPBits(UP::build_quiet_nan(false, v)).get_val();
LIBC_INLINE static constexpr T build_quiet_nan(StorageType v,
Sign sign = Sign::POS) {
return FPBits(UP::build_quiet_nan(sign, v)).get_val();
}

// TODO: Use an uint32_t for 'biased_exp'.
LIBC_INLINE static constexpr FPBits<T>
create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
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(
Expand Down
16 changes: 8 additions & 8 deletions libc/src/__support/FPUtil/ManipulationFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,13 @@ LIBC_INLINE T modf(T x, T &iptr) {
return x;
} else if (bits.is_inf()) {
iptr = x;
return bits.get_sign() ? T(FPBits<T>::neg_zero()) : T(FPBits<T>::zero());
return T(FPBits<T>::zero(bits.sign()));
} else {
iptr = trunc(x);
if (x == iptr) {
// If x is already an integer value, then return zero with the right
// sign.
return bits.get_sign() ? T(FPBits<T>::neg_zero()) : T(FPBits<T>::zero());
return T(FPBits<T>::zero(bits.sign()));
} else {
return x - iptr;
}
Expand All @@ -65,7 +65,7 @@ LIBC_INLINE T modf(T x, T &iptr) {
template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE T copysign(T x, T y) {
FPBits<T> xbits(x);
xbits.set_sign(FPBits<T>(y).get_sign());
xbits.set_sign(FPBits<T>(y).sign());
return T(xbits);
}

Expand Down Expand Up @@ -103,12 +103,12 @@ LIBC_INLINE T logb(T x) {
if (bits.is_zero()) {
// TODO(Floating point exception): Raise div-by-zero exception.
// TODO(errno): POSIX requires setting errno to ERANGE.
return T(FPBits<T>::neg_inf());
return T(FPBits<T>::inf(Sign::NEG));
} else if (bits.is_nan()) {
return x;
} else if (bits.is_inf()) {
// Return positive infinity.
return T(FPBits<T>::inf());
return T(FPBits<T>::inf(Sign::POS));
}

NormalFloat<T> normal(bits);
Expand All @@ -131,11 +131,11 @@ LIBC_INLINE T ldexp(T x, int exp) {
// calculating the limit.
int exp_limit = FPBits<T>::MAX_BIASED_EXPONENT + FPBits<T>::FRACTION_LEN + 1;
if (exp > exp_limit)
return bits.get_sign() ? T(FPBits<T>::neg_inf()) : T(FPBits<T>::inf());
return T(FPBits<T>::inf(bits.sign()));

// Similarly on the negative side we return zero early if |exp| is too small.
if (exp < -exp_limit)
return bits.get_sign() ? T(FPBits<T>::neg_zero()) : T(FPBits<T>::zero());
return T(FPBits<T>::zero(bits.sign()));

// For all other values, NormalFloat to T conversion handles it the right way.
NormalFloat<T> normal(bits);
Expand Down Expand Up @@ -173,7 +173,7 @@ LIBC_INLINE T nextafter(T from, U to) {
}
} else {
int_val = FPBits<T>::MIN_SUBNORMAL;
if (to_bits.get_sign())
if (to_bits.is_neg())
int_val |= FPBits<T>::SIGN_MASK;
}

Expand Down
Loading