[libc-commits] [libc] [libc][NFC] Simplify `FPBits` (PR #76835)
Guillaume Chatelet via libc-commits
libc-commits at lists.llvm.org
Thu Jan 4 06:57:18 PST 2024
https://github.com/gchatelet updated https://github.com/llvm/llvm-project/pull/76835
>From d6d4b268ac3508202cff322529b4740fedfd2379 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 15:10:43 +0000
Subject: [PATCH 01/13] Use uintval() to access bits
---
libc/src/__support/FPUtil/fpbits_str.h | 2 +-
libc/test/src/stdlib/strtold_test.cpp | 2 +-
libc/test/src/time/difftime_test.cpp | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/libc/src/__support/FPUtil/fpbits_str.h b/libc/src/__support/FPUtil/fpbits_str.h
index ce368c89f95ef7..50019f32b2c446 100644
--- a/libc/src/__support/FPUtil/fpbits_str.h
+++ b/libc/src/__support/FPUtil/fpbits_str.h
@@ -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: ";
diff --git a/libc/test/src/stdlib/strtold_test.cpp b/libc/test/src/stdlib/strtold_test.cpp
index 37db385c959bf5..86ac8a6e26d7c0 100644
--- a/libc/test/src/stdlib/strtold_test.cpp
+++ b/libc/test/src/stdlib/strtold_test.cpp
@@ -87,7 +87,7 @@ class LlvmLibcStrToLDTest : public LIBC_NAMESPACE::testing::Test {
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());
diff --git a/libc/test/src/time/difftime_test.cpp b/libc/test/src/time/difftime_test.cpp
index 187df4add1e727..9c3e8d3f30f2b9 100644
--- a/libc/test/src/time/difftime_test.cpp
+++ b/libc/test/src/time/difftime_test.cpp
@@ -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());
>From 078debba4682b9ebe05799b36db8f4e272ba8cb4 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 15:12:07 +0000
Subject: [PATCH 02/13] Move storage and functions to base class
---
libc/src/__support/FPUtil/FPBits.h | 84 ++++++++-----------
.../__support/FPUtil/x86_64/LongDoubleBits.h | 34 +++-----
2 files changed, 47 insertions(+), 71 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 8304b76d3d8ad0..b864b30277887f 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -87,9 +87,9 @@ template <> struct FPLayout<FPType::X86_Binary80> {
} // 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,7 +149,7 @@ struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
return StorageType(1) << position;
}
-public:
+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
@@ -159,7 +159,6 @@ struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
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 =
@@ -173,41 +172,11 @@ struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
UP::ENCODING == internal::FPEncoding::X86_ExtendedPrecision
? 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;
@@ -266,6 +235,23 @@ template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
}
};
+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 : public FPRepBase<fp_type> {
+ using UP = FPRepBase<fp_type>;
+ using typename UP::StorageType;
+ using UP::EXP_BIAS;
+ using UP::EXP_LEN;
+ using UP::FRACTION_LEN;
+ using UP::FRACTION_MASK;
+ using UP::MANTISSA_PRECISION;
+ using UP::SIGN_MASK;
+ using UP::STORAGE_LEN;
+ using UP::TOTAL_LEN;
+};
+
} // namespace internal
// Returns the FPType corresponding to C++ type T on the host.
@@ -311,14 +297,14 @@ 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;
public:
+ using StorageType = typename UP::StorageType;
+ using UP::bits;
using UP::EXP_BIAS;
using UP::EXP_LEN;
using UP::EXP_MASK;
@@ -327,6 +313,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
using UP::FRACTION_MASK;
using UP::SIGN_MASK;
using UP::TOTAL_LEN;
+ using UP::UP;
using UP::get_biased_exponent;
using UP::is_zero;
@@ -347,17 +334,18 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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() = default;
- LIBC_INLINE constexpr FPBits() : UP() {}
+ 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 {
+ static_assert(cpp::always_false<XType>);
+ }
+ }
LIBC_INLINE constexpr void set_val(T value) {
bits = cpp::bit_cast<StorageType>(value);
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 4dc5d25e269820..e0ed38d9bbd73a 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -30,23 +30,13 @@ 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;
static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
// Subnormal numbers include the implicit bit in x86 long double formats.
@@ -73,21 +63,19 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
}
- LIBC_INLINE constexpr FPBits() : UP() {}
+ LIBC_INLINE constexpr FPBits() = default;
- 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> 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 {
+ static_assert(cpp::always_false<XType>);
+ }
}
- 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);
}
>From da77e5640357dd75e9a015100d28d1b17ed13346 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 15:15:24 +0000
Subject: [PATCH 03/13] Remove FPEncoding
---
libc/src/__support/FPUtil/FPBits.h | 19 +++----------------
1 file changed, 3 insertions(+), 16 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index b864b30277887f..41c52567df3765 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -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,8 +71,6 @@ 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
@@ -152,8 +140,7 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
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 =
@@ -162,14 +149,14 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
// 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...
>From c6815ecd113081007c68ba8e3aa8a0d446873e9c Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 15:18:56 +0000
Subject: [PATCH 04/13] Reorder functions
---
libc/src/__support/FPUtil/FPBits.h | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 41c52567df3765..6447f3f3cc6843 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -164,23 +164,27 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
StorageType bits = 0;
public:
- LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
- mantVal &= FRACTION_MASK;
- bits &= ~FRACTION_MASK;
- bits |= mantVal;
+ LIBC_INLINE constexpr bool get_sign() const {
+ return (bits & SIGN_MASK) != 0;
+ }
+
+ LIBC_INLINE constexpr void set_sign(bool signVal) {
+ if (get_sign() != signVal)
+ bits ^= SIGN_MASK;
}
LIBC_INLINE constexpr StorageType get_mantissa() const {
return bits & FRACTION_MASK;
}
- LIBC_INLINE constexpr void set_sign(bool signVal) {
- if (get_sign() != signVal)
- bits ^= SIGN_MASK;
+ LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
+ mantVal &= FRACTION_MASK;
+ bits &= ~FRACTION_MASK;
+ bits |= mantVal;
}
- LIBC_INLINE constexpr bool get_sign() const {
- return (bits & SIGN_MASK) != 0;
+ 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) {
@@ -190,10 +194,6 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
}
- LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
- return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
- }
-
LIBC_INLINE constexpr int get_exponent() const {
return int(get_biased_exponent()) - EXP_BIAS;
}
>From ac4ca63fceec768731cf854b4815ad7394342466 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 15:23:21 +0000
Subject: [PATCH 05/13] Reduce FPRep symbol export and add documentation
---
libc/src/__support/FPUtil/FPBits.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 6447f3f3cc6843..d23d29b8727345 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -224,19 +224,19 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
namespace internal {
-// This is a temporary class to unify common methods and properties between
-// FPBits and FPBits<long double>.
+// 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::EXP_BIAS;
- using UP::EXP_LEN;
using UP::FRACTION_LEN;
using UP::FRACTION_MASK;
using UP::MANTISSA_PRECISION;
- using UP::SIGN_MASK;
- using UP::STORAGE_LEN;
- using UP::TOTAL_LEN;
};
} // namespace internal
>From deb2b67cb759dd5838361335b7e1dd1d97077fca Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:06:19 +0000
Subject: [PATCH 06/13] Standardize FPBits constants
---
libc/src/__support/FPUtil/FPBits.h | 4 +++-
.../__support/FPUtil/x86_64/LongDoubleBits.h | 21 ++++++++++---------
2 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index d23d29b8727345..0c641167c26cf4 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -288,6 +288,8 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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;
@@ -319,7 +321,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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;
+ (StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
LIBC_INLINE constexpr FPBits() = default;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index e0ed38d9bbd73a..a2bf264064b4d6 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -37,20 +37,21 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
using UP::QUIET_NAN_MASK;
public:
- static constexpr int MAX_BIASED_EXPONENT = 0x7FFF;
+ 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;
+ static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0, "mask disjoint");
+ static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK, "mask cover");
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);
}
>From eb26cc66debb6a8f60aee2635c3efaf75d82d453 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:14:29 +0000
Subject: [PATCH 07/13] Simplify long double implementation
---
libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index a2bf264064b4d6..23917d69a37cdb 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -52,16 +52,16 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
(StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
- return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
+ return bits & SIG_MASK;
}
- LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
- bits &= ~(StorageType(1) << FRACTION_LEN);
- bits |= (StorageType(implicitVal) << FRACTION_LEN);
+ LIBC_INLINE constexpr bool get_implicit_bit() const {
+ return bits & EXPLICIT_BIT_MASK;
}
- LIBC_INLINE constexpr bool get_implicit_bit() const {
- return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
+ LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
+ if (get_implicit_bit() != implicitVal)
+ bits ^= EXPLICIT_BIT_MASK;
}
LIBC_INLINE constexpr FPBits() = default;
>From 01d1471d5cc40798a8ccb545de43aac7d27076d2 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:18:24 +0000
Subject: [PATCH 08/13] Reorder FPBits functions
---
libc/src/__support/FPUtil/FPBits.h | 22 +++++++------
.../__support/FPUtil/x86_64/LongDoubleBits.h | 33 ++++++++++---------
2 files changed, 30 insertions(+), 25 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 0c641167c26cf4..035e1c26f7d35f 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -306,16 +306,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
using UP::get_biased_exponent;
using UP::is_zero;
-
- // The function return mantissa with the implicit bit set iff the current
- // value is a valid normal number.
- LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
- return ((get_biased_exponent() > 0 && !is_inf_or_nan())
- ? (FRACTION_MASK + 1)
- : 0) |
- (FRACTION_MASK & bits);
- }
-
+ // 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;
@@ -323,6 +314,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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) {
@@ -336,6 +328,16 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
}
}
+ // The function return mantissa with the implicit bit set iff the current
+ // value is a valid normal number.
+ LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
+ return ((get_biased_exponent() > 0 && !is_inf_or_nan())
+ ? (FRACTION_MASK + 1)
+ : 0) |
+ (FRACTION_MASK & bits);
+ }
+
+ // Floating-point conversions.
LIBC_INLINE constexpr void set_val(T value) {
bits = cpp::bit_cast<StorageType>(value);
}
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 23917d69a37cdb..40743c42947db1 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -37,6 +37,7 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
using UP::QUIET_NAN_MASK;
public:
+ // 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.
@@ -51,19 +52,7 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
static constexpr StorageType MAX_NORMAL =
(StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
- LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
- return bits & SIG_MASK;
- }
-
- LIBC_INLINE constexpr bool get_implicit_bit() const {
- return bits & EXPLICIT_BIT_MASK;
- }
-
- LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
- if (get_implicit_bit() != implicitVal)
- bits ^= EXPLICIT_BIT_MASK;
- }
-
+ // Constructors.
LIBC_INLINE constexpr FPBits() = default;
template <typename XType> LIBC_INLINE constexpr explicit FPBits(XType x) {
@@ -77,14 +66,28 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
}
}
- LIBC_INLINE constexpr operator long double() {
+ // Floating-point conversions.
+ LIBC_INLINE constexpr long double get_val() const {
return cpp::bit_cast<long double>(bits);
}
- LIBC_INLINE constexpr long double get_val() const {
+ LIBC_INLINE constexpr operator long double() {
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 bits & EXPLICIT_BIT_MASK;
+ }
+
+ 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 {
return get_biased_exponent() == MAX_BIASED_EXPONENT &&
get_mantissa() == 0 && get_implicit_bit() == 1;
>From 2684e6d38555295256815cad3f25bb9367e4b6ea Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:19:17 +0000
Subject: [PATCH 09/13] Make long double cast operator const
---
libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 40743c42947db1..a749876894688c 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -71,7 +71,7 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
return cpp::bit_cast<long double>(bits);
}
- LIBC_INLINE constexpr operator long double() {
+ LIBC_INLINE constexpr operator long double() const {
return cpp::bit_cast<long double>(bits);
}
>From f5239d13f6b6119e7611a8fbc71057238489510d Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:22:16 +0000
Subject: [PATCH 10/13] Remove low value `set_val` function
---
libc/src/__support/FPUtil/FPBits.h | 8 ++------
libc/src/math/generic/log.cpp | 2 +-
libc/src/math/generic/log10.cpp | 2 +-
libc/src/math/generic/log10f.cpp | 2 +-
libc/src/math/generic/log2.cpp | 2 +-
libc/src/math/generic/log2f.cpp | 2 +-
libc/src/math/generic/logf.cpp | 2 +-
7 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 035e1c26f7d35f..a8a5a31b4d5b38 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -314,7 +314,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
static constexpr StorageType MAX_NORMAL =
(StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
-// Constructors.
+ // Constructors.
LIBC_INLINE constexpr FPBits() = default;
template <typename XType> LIBC_INLINE constexpr explicit FPBits(XType x) {
@@ -337,11 +337,7 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
(FRACTION_MASK & bits);
}
- // Floating-point conversions.
- LIBC_INLINE constexpr void set_val(T value) {
- bits = cpp::bit_cast<StorageType>(value);
- }
-
+ // 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(); }
diff --git a/libc/src/math/generic/log.cpp b/libc/src/math/generic/log.cpp
index f82683bcc05544..8f22fdea93217f 100644
--- a/libc/src/math/generic/log.cpp
+++ b/libc/src/math/generic/log.cpp
@@ -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();
}
diff --git a/libc/src/math/generic/log10.cpp b/libc/src/math/generic/log10.cpp
index d2b94f22687fb8..df82f24dc6967c 100644
--- a/libc/src/math/generic/log10.cpp
+++ b/libc/src/math/generic/log10.cpp
@@ -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();
}
diff --git a/libc/src/math/generic/log10f.cpp b/libc/src/math/generic/log10f.cpp
index b70183958899cc..da69f7f5ad4d9e 100644
--- a/libc/src/math/generic/log10f.cpp
+++ b/libc/src/math/generic/log10f.cpp
@@ -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();
}
diff --git a/libc/src/math/generic/log2.cpp b/libc/src/math/generic/log2.cpp
index a333a075fe5aff..1427d1934db900 100644
--- a/libc/src/math/generic/log2.cpp
+++ b/libc/src/math/generic/log2.cpp
@@ -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();
}
diff --git a/libc/src/math/generic/log2f.cpp b/libc/src/math/generic/log2f.cpp
index e7aeda723b50a8..07dedba85e6273 100644
--- a/libc/src/math/generic/log2f.cpp
+++ b/libc/src/math/generic/log2f.cpp
@@ -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;
}
diff --git a/libc/src/math/generic/logf.cpp b/libc/src/math/generic/logf.cpp
index 2ebdddbb2d16da..f1f93468479b11 100644
--- a/libc/src/math/generic/logf.cpp
+++ b/libc/src/math/generic/logf.cpp
@@ -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();
}
>From 2e80400d68632fb245c33331d670badd4a87d692 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 16:59:12 +0000
Subject: [PATCH 11/13] Make representation explicit when building numbers
---
libc/src/__support/FPUtil/FPBits.h | 57 ++++++++++---------
.../__support/FPUtil/x86_64/LongDoubleBits.h | 51 ++++++++---------
2 files changed, 54 insertions(+), 54 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index a8a5a31b4d5b38..6565adc2075b95 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -136,6 +136,11 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
LIBC_INLINE static constexpr StorageType bit_at(int position) {
return StorageType(1) << position;
}
+ 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.
@@ -178,9 +183,7 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
}
LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
- mantVal &= FRACTION_MASK;
- bits &= ~FRACTION_MASK;
- bits |= mantVal;
+ bits = merge(bits, mantVal, FRACTION_MASK);
}
LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
@@ -188,10 +191,7 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
}
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;
+ bits = merge(bits, biased << EXP_MASK_SHIFT, EXP_MASK);
}
LIBC_INLINE constexpr int get_exponent() const {
@@ -327,6 +327,10 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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.
@@ -337,11 +341,6 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
(FRACTION_MASK & bits);
}
- // 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(); }
-
LIBC_INLINE constexpr bool is_inf() const {
return (bits & EXP_SIG_MASK) == EXP_MASK;
}
@@ -362,14 +361,20 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
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);
+ 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); }
@@ -391,15 +396,24 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
}
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)
@@ -427,15 +441,6 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
}
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
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index a749876894688c..4ff360be4d9b35 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -109,34 +109,26 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
// 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();
}
@@ -153,13 +145,16 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
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);
}
};
>From 8f375a517a29e5c90cb1f2a392d33d2e372b9774 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 3 Jan 2024 17:18:25 +0000
Subject: [PATCH 12/13] Use the same representation decomposition for `zero`
---
libc/src/__support/FPUtil/FPBits.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 6565adc2075b95..88083cf3d0d0f5 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -364,7 +364,9 @@ 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) {
- StorageType rep = sign ? SIGN_MASK : StorageType(0);
+ StorageType rep = (sign ? SIGN_MASK : StorageType(0)) // sign
+ | 0 // exponent
+ | 0; // mantissa
return FPBits(rep).get_val();
}
>From 1d78e13b12793357c84e078927db79bf52b2ad1c Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Thu, 4 Jan 2024 14:56:57 +0000
Subject: [PATCH 13/13] Address comments
---
libc/src/__support/FPUtil/FPBits.h | 6 ++++++
libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 11 +++++++++--
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 88083cf3d0d0f5..63eeba85f15e5c 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -136,6 +136,10 @@ struct FPRepBase : public internal::FPLayout<fp_type> {
LIBC_INLINE static constexpr StorageType bit_at(int position) {
return StorageType(1) << position;
}
+
+ // 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
@@ -324,6 +328,8 @@ template <typename T> struct FPBits : public internal::FPRep<get_fp_type<T>()> {
} 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>);
}
}
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 4ff360be4d9b35..8abc0c87af0d20 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -43,9 +43,14 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
// explicitly. This is the mask for that bit.
static constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1)
<< FRACTION_LEN;
- static_assert((EXPLICIT_BIT_MASK & FRACTION_MASK) == 0, "mask disjoint");
- static_assert((EXPLICIT_BIT_MASK | FRACTION_MASK) == SIG_MASK, "mask cover");
+ // 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 = FRACTION_MASK;
static constexpr StorageType MIN_NORMAL =
(StorageType(1) << SIG_LEN) | EXPLICIT_BIT_MASK;
@@ -62,6 +67,8 @@ struct FPBits<long double> : public internal::FPRep<FPType::X86_Binary80> {
} 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>);
}
}
More information about the libc-commits
mailing list