[libc-commits] [libc] [libc][NFC] Unify `FPBits` implementations (PR #76033)

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Wed Dec 20 05:36:59 PST 2023


https://github.com/gchatelet updated https://github.com/llvm/llvm-project/pull/76033

>From f802c4010c62702a4afa28597ac90b697afc681f Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:03:59 +0000
Subject: [PATCH 01/18] [libc][NFC] Unify `FPBits` implementations

`FPBits` is currently implemented as a general case and a specialization for `long double` when `long double` is x86 80-bit extended precision. This patch is a first of a series to provide a single implementation based on `FPType` instead of the C++ float type (which implementation is architecture dependent).
---
 libc/src/__support/FPUtil/FPBits.h            | 86 ++++++++++++-------
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 44 ++++------
 2 files changed, 74 insertions(+), 56 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 37e2820eab8553..bc79930797b691 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -20,41 +20,36 @@
 namespace LIBC_NAMESPACE {
 namespace fputil {
 
-// A generic class to represent single precision, double precision, and quad
-// precision IEEE 754 floating point formats.
-// On most platforms, the 'float' type corresponds to single precision floating
-// point numbers, the 'double' type corresponds to double precision floating
-// point numers, and the 'long double' type corresponds to the quad precision
-// floating numbers. On x86 platforms however, the 'long double' type maps to
-// an x87 floating point format. This format is an IEEE 754 extension format.
-// It is handled as an explicit specialization of this class.
-template <typename T> struct FPBits : private FloatProperties<T> {
-  static_assert(cpp::is_floating_point_v<T>,
-                "FPBits instantiated with invalid type.");
-  using typename FloatProperties<T>::StorageType;
-  using FloatProperties<T>::TOTAL_LEN;
-
-private:
-  using FloatProperties<T>::EXP_SIG_MASK;
+namespace internal {
 
-public:
-  using FloatProperties<T>::EXP_MASK;
-  using FloatProperties<T>::EXP_BIAS;
-  using FloatProperties<T>::EXP_LEN;
-  using FloatProperties<T>::FRACTION_MASK;
-  using FloatProperties<T>::FRACTION_LEN;
+// This is a temporary class to unify common methods and properties between
+// FPBits and FPBits<long double>.
+template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
+  using UP = FPProperties<fp_type>;
+  using typename UP::StorageType;
+  using UP::TOTAL_LEN;
 
-private:
-  using FloatProperties<T>::QUIET_NAN_MASK;
+protected:
+  using UP::EXP_SIG_MASK;
+  using UP::QUIET_NAN_MASK;
 
 public:
-  using FloatProperties<T>::SIGN_MASK;
+  using UP::EXP_BIAS;
+  using UP::EXP_LEN;
+  using UP::EXP_MASK;
+  using UP::FP_MASK;
+  using UP::FRACTION_LEN;
+  using UP::FRACTION_MASK;
+  using UP::SIGN_MASK;
 
   // 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 FPBitsCommon() : bits(0) {}
+  LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
+
   LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
     mantVal &= FRACTION_MASK;
     bits &= ~FRACTION_MASK;
@@ -64,6 +59,38 @@ template <typename T> struct FPBits : private FloatProperties<T> {
   LIBC_INLINE constexpr StorageType get_mantissa() const {
     return bits & FRACTION_MASK;
   }
+};
+
+} // namespace internal
+
+// A generic class to represent single precision, double precision, and quad
+// precision IEEE 754 floating point formats.
+// On most platforms, the 'float' type corresponds to single precision floating
+// point numbers, the 'double' type corresponds to double precision floating
+// point numers, and the 'long double' type corresponds to the quad precision
+// floating numbers. On x86 platforms however, the 'long double' type maps to
+// an x87 floating point format. This format is an IEEE 754 extension format.
+// It is handled as an explicit specialization of this class.
+template <typename T>
+struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
+  static_assert(cpp::is_floating_point_v<T>,
+                "FPBits instantiated with invalid type.");
+  using UP = internal::FPBitsCommon<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 UP::EXP_BIAS;
+  using UP::EXP_LEN;
+  using UP::EXP_MASK;
+  using UP::FRACTION_LEN;
+  using UP::FRACTION_MASK;
+  using UP::SIGN_MASK;
+  using UP::TOTAL_LEN;
 
   LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
     expVal = (expVal << FRACTION_LEN) & EXP_MASK;
@@ -94,9 +121,6 @@ template <typename T> struct FPBits : private FloatProperties<T> {
     return (bits & SIGN_MASK) != 0;
   }
 
-  static_assert(sizeof(T) == sizeof(StorageType),
-                "Data type and integral representation have different sizes.");
-
   static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
   static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
   static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
@@ -108,13 +132,13 @@ template <typename T> struct FPBits : private FloatProperties<T> {
   // type match.
   template <typename XType, cpp::enable_if_t<cpp::is_same_v<T, XType>, int> = 0>
   LIBC_INLINE constexpr explicit FPBits(XType x)
-      : bits(cpp::bit_cast<StorageType>(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) : bits(x) {}
+  LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
 
-  LIBC_INLINE constexpr FPBits() : bits(0) {}
+  LIBC_INLINE constexpr FPBits() : UP() {}
 
   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
 
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index b2b016adb661a7..9c67ef6126f6be 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -26,20 +26,26 @@
 namespace LIBC_NAMESPACE {
 namespace fputil {
 
-template <> struct FPBits<long double> : private FloatProperties<long double> {
-  using typename FloatProperties<long double>::StorageType;
-  using FloatProperties<long double>::TOTAL_LEN;
-  using FloatProperties<long double>::EXP_MASK;
-  using FloatProperties<long double>::EXP_BIAS;
-  using FloatProperties<long double>::EXP_LEN;
-  using FloatProperties<long double>::FRACTION_MASK;
-  using FloatProperties<long double>::FRACTION_LEN;
+template <>
+struct FPBits<long double>
+    : public internal::FPBitsCommon<FPType::X86_Binary80> {
+  using UP = internal::FPBitsCommon<FPType::X86_Binary80>;
+  using StorageType = typename UP::StorageType;
+  using UP::bits;
 
 private:
-  using FloatProperties<long double>::QUIET_NAN_MASK;
+  using UP::EXP_SIG_MASK;
+  using UP::QUIET_NAN_MASK;
 
 public:
-  using FloatProperties<long double>::SIGN_MASK;
+  using UP::EXP_BIAS;
+  using UP::EXP_LEN;
+  using UP::EXP_MASK;
+  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);
@@ -51,18 +57,6 @@ template <> struct FPBits<long double> : private FloatProperties<long double> {
       (StorageType(MAX_BIASED_EXPONENT - 1) << (FRACTION_LEN + 1)) |
       (StorageType(1) << FRACTION_LEN) | MAX_SUBNORMAL;
 
-  StorageType bits;
-
-  LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
-    mantVal &= FRACTION_MASK;
-    bits &= ~FRACTION_MASK;
-    bits |= mantVal;
-  }
-
-  LIBC_INLINE constexpr StorageType get_mantissa() const {
-    return bits & FRACTION_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.
@@ -99,12 +93,12 @@ template <> struct FPBits<long double> : private FloatProperties<long double> {
     return bool((bits & SIGN_MASK) >> (TOTAL_LEN - 1));
   }
 
-  LIBC_INLINE constexpr FPBits() : bits(0) {}
+  LIBC_INLINE constexpr FPBits() : UP() {}
 
   template <typename XType,
             cpp::enable_if_t<cpp::is_same_v<long double, XType>, int> = 0>
   LIBC_INLINE constexpr explicit FPBits(XType x)
-      : bits(cpp::bit_cast<StorageType>(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);
@@ -112,7 +106,7 @@ template <> struct FPBits<long double> : private FloatProperties<long double> {
 
   template <typename XType,
             cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
-  LIBC_INLINE constexpr explicit FPBits(XType x) : bits(x) {}
+  LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
 
   LIBC_INLINE constexpr operator long double() {
     return cpp::bit_cast<long double>(bits);

>From 04983437ea45aa5819d464d37986681a3f5e4022 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:14:15 +0000
Subject: [PATCH 02/18] Unify (get|set)_sign

---
 libc/src/__support/FPUtil/FPBits.h                | 5 ++---
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 7 +++----
 2 files changed, 5 insertions(+), 7 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index bc79930797b691..9e088f91e96544 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -112,9 +112,8 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   }
 
   LIBC_INLINE constexpr void set_sign(bool signVal) {
-    bits |= SIGN_MASK;
-    if (!signVal)
-      bits -= SIGN_MASK;
+    if (get_sign() != signVal)
+      bits ^= SIGN_MASK;
   }
 
   LIBC_INLINE constexpr bool get_sign() const {
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 9c67ef6126f6be..daeda7f04357f8 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -84,13 +84,12 @@ struct FPBits<long double>
   }
 
   LIBC_INLINE constexpr void set_sign(bool signVal) {
-    bits &= ~SIGN_MASK;
-    StorageType sign1 = StorageType(signVal) << (TOTAL_LEN - 1);
-    bits |= sign1;
+    if (get_sign() != signVal)
+      bits ^= SIGN_MASK;
   }
 
   LIBC_INLINE constexpr bool get_sign() const {
-    return bool((bits & SIGN_MASK) >> (TOTAL_LEN - 1));
+    return (bits & SIGN_MASK) != 0;
   }
 
   LIBC_INLINE constexpr FPBits() : UP() {}

>From 5d616669693723e60f3c5b3a217af40e4a7b4309 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:15:23 +0000
Subject: [PATCH 03/18] Move (get|set)_sign to common implementation

---
 libc/src/__support/FPUtil/FPBits.h             | 18 +++++++++---------
 .../__support/FPUtil/x86_64/LongDoubleBits.h   |  9 ---------
 2 files changed, 9 insertions(+), 18 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 9e088f91e96544..fb778c8adf9a3d 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -59,6 +59,15 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   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 bool get_sign() const {
+    return (bits & SIGN_MASK) != 0;
+  }
 };
 
 } // namespace internal
@@ -111,15 +120,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
            (FRACTION_MASK & bits);
   }
 
-  LIBC_INLINE constexpr void set_sign(bool signVal) {
-    if (get_sign() != signVal)
-      bits ^= SIGN_MASK;
-  }
-
-  LIBC_INLINE constexpr bool get_sign() const {
-    return (bits & SIGN_MASK) != 0;
-  }
-
   static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
   static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
   static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index daeda7f04357f8..2c1162d4f2e151 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -83,15 +83,6 @@ struct FPBits<long double>
     return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
   }
 
-  LIBC_INLINE constexpr void set_sign(bool signVal) {
-    if (get_sign() != signVal)
-      bits ^= SIGN_MASK;
-  }
-
-  LIBC_INLINE constexpr bool get_sign() const {
-    return (bits & SIGN_MASK) != 0;
-  }
-
   LIBC_INLINE constexpr FPBits() : UP() {}
 
   template <typename XType,

>From a19ec2d594e7160059a812347f458a12881c8d46 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:17:55 +0000
Subject: [PATCH 04/18] Move uintval() to common implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 8 ++++----
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 5 -----
 2 files changed, 4 insertions(+), 9 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index fb778c8adf9a3d..b4d3d0581726d5 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -68,6 +68,8 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   LIBC_INLINE constexpr bool get_sign() const {
     return (bits & SIGN_MASK) != 0;
   }
+
+  LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
 };
 
 } // namespace internal
@@ -139,15 +141,13 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
 
   LIBC_INLINE constexpr FPBits() : UP() {}
 
-  LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
-
   LIBC_INLINE constexpr void set_val(T value) {
     bits = cpp::bit_cast<StorageType>(value);
   }
 
-  LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
+  LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
 
-  LIBC_INLINE constexpr StorageType uintval() const { return bits; }
+  LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
 
   LIBC_INLINE constexpr int get_exponent() const {
     return int(get_biased_exponent()) - EXP_BIAS;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 2c1162d4f2e151..8e6c86ff53a753 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -102,11 +102,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr StorageType uintval() {
-    // We zero the padding bits as they can contain garbage.
-    return bits & FP_MASK;
-  }
-
   LIBC_INLINE constexpr long double get_val() const {
     return cpp::bit_cast<long double>(bits);
   }

>From 8f32f4a03072efb0b80d9a2b4389099d31fd6eea Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:25:10 +0000
Subject: [PATCH 05/18] Unify (get|set)_biased_exponent

---
 libc/src/__support/FPUtil/FPBits.h                | 11 +++++++----
 libc/src/__support/FPUtil/FloatProperties.h       |  2 +-
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 10 ++++++----
 3 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index b4d3d0581726d5..541bac1b0a17d3 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -37,6 +37,7 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   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;
@@ -98,19 +99,21 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   using UP::EXP_BIAS;
   using UP::EXP_LEN;
   using UP::EXP_MASK;
+  using UP::EXP_MASK_SHIFT;
   using UP::FRACTION_LEN;
   using UP::FRACTION_MASK;
   using UP::SIGN_MASK;
   using UP::TOTAL_LEN;
 
-  LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
-    expVal = (expVal << FRACTION_LEN) & EXP_MASK;
+  LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
+    // clear exponent bits
     bits &= ~EXP_MASK;
-    bits |= expVal;
+    // set exponent bits
+    bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
   }
 
   LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
-    return uint16_t((bits & EXP_MASK) >> FRACTION_LEN);
+    return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
   }
 
   // The function return mantissa with the implicit bit set iff the current
diff --git a/libc/src/__support/FPUtil/FloatProperties.h b/libc/src/__support/FPUtil/FloatProperties.h
index bcf1f7cfabd34b..6bf75b7167d32d 100644
--- a/libc/src/__support/FPUtil/FloatProperties.h
+++ b/libc/src/__support/FPUtil/FloatProperties.h
@@ -111,7 +111,7 @@ struct FPProperties : public internal::FPBaseProperties<fp_type> {
       (1U << (EXP_LEN - 1U)) - 1U;
   static_assert(EXP_BIAS > 0);
 
-private:
+protected:
   // The shift amount to get the *significand* part to the least significant
   // bit. Always `0` but kept for consistency.
   LIBC_INLINE_VAR static constexpr int SIG_MASK_SHIFT = 0;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 8e6c86ff53a753..88e3df8b66f103 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -41,6 +41,7 @@ struct FPBits<long double>
   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;
@@ -64,14 +65,15 @@ struct FPBits<long double>
     return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
   }
 
-  LIBC_INLINE constexpr void set_biased_exponent(StorageType expVal) {
-    expVal = (expVal << (TOTAL_LEN - 1 - EXP_LEN)) & EXP_MASK;
+  LIBC_INLINE constexpr void set_biased_exponent(StorageType biased) {
+    // clear exponent bits
     bits &= ~EXP_MASK;
-    bits |= expVal;
+    // set exponent bits
+    bits |= (biased << EXP_MASK_SHIFT) & EXP_MASK;
   }
 
   LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
-    return uint16_t((bits & EXP_MASK) >> (TOTAL_LEN - 1 - EXP_LEN));
+    return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
   }
 
   LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {

>From 5dbb7ee4d993728ad95501e31a5f9c273e5c2bc1 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:27:02 +0000
Subject: [PATCH 06/18] Move (get|set)_biased_exponent to common implementation

---
 libc/src/__support/FPUtil/FPBits.h            | 22 ++++++++++---------
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 11 ----------
 2 files changed, 12 insertions(+), 21 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 541bac1b0a17d3..a30d8dbd40a617 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -70,6 +70,17 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
     return (bits & SIGN_MASK) != 0;
   }
 
+  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;
+  }
+
+  LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
+    return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
+  }
+
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
 };
 
@@ -105,16 +116,7 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   using UP::SIGN_MASK;
   using UP::TOTAL_LEN;
 
-  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;
-  }
-
-  LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
-    return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
-  }
+  using UP::get_biased_exponent;
 
   // The function return mantissa with the implicit bit set iff the current
   // value is a valid normal number.
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 88e3df8b66f103..73f966e115ea74 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -65,17 +65,6 @@ struct FPBits<long double>
     return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
   }
 
-  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;
-  }
-
-  LIBC_INLINE constexpr uint16_t get_biased_exponent() const {
-    return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
-  }
-
   LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
     bits &= ~(StorageType(1) << FRACTION_LEN);
     bits |= (StorageType(implicitVal) << FRACTION_LEN);

>From 36859e094967c197f2c22382862d18a256660625 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:28:05 +0000
Subject: [PATCH 07/18] Move get_exponent to common implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 8 ++++----
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 4 ----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index a30d8dbd40a617..244bbd6efc00e4 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -81,6 +81,10 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
     return uint16_t((bits & EXP_MASK) >> EXP_MASK_SHIFT);
   }
 
+  LIBC_INLINE constexpr int get_exponent() const {
+    return int(get_biased_exponent()) - EXP_BIAS;
+  }
+
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
 };
 
@@ -154,10 +158,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
 
   LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
 
-  LIBC_INLINE constexpr int get_exponent() const {
-    return int(get_biased_exponent()) - EXP_BIAS;
-  }
-
   // If the number is subnormal, the exponent is treated as if it were the
   // minimum exponent for a normal number. This is to keep continuity between
   // the normal and subnormal ranges, but it causes problems for functions where
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 73f966e115ea74..0b7341fb545f88 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -97,10 +97,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr int get_exponent() const {
-    return int(get_biased_exponent()) - EXP_BIAS;
-  }
-
   // If the number is subnormal, the exponent is treated as if it were the
   // minimum exponent for a normal number. This is to keep continuity between
   // the normal and subnormal ranges, but it causes problems for functions where

>From 59590ce3a94978a3497d50c8f9256225b80bf47e Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:32:33 +0000
Subject: [PATCH 08/18] Unify is_zero implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 3 +--
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 3 +--
 2 files changed, 2 insertions(+), 4 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 244bbd6efc00e4..1a4f6b7c6d11a4 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -176,8 +176,7 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   }
 
   LIBC_INLINE constexpr bool is_zero() const {
-    // Remove sign bit by shift
-    return (bits << 1) == 0;
+    return (bits & EXP_SIG_MASK) == 0;
   }
 
   LIBC_INLINE constexpr bool is_inf() const {
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 0b7341fb545f88..cb120f898c2e34 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -115,8 +115,7 @@ struct FPBits<long double>
   }
 
   LIBC_INLINE constexpr bool is_zero() const {
-    return get_biased_exponent() == 0 && get_mantissa() == 0 &&
-           get_implicit_bit() == 0;
+    return (bits & EXP_SIG_MASK) == 0;
   }
 
   LIBC_INLINE constexpr bool is_inf() const {

>From 6a750393655154a95e93cf8cb4e52a338ed1d0eb Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:34:35 +0000
Subject: [PATCH 09/18] Move is_zero to common implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 9 +++++----
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h | 4 ----
 2 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 1a4f6b7c6d11a4..3ec471f2e9072b 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -86,6 +86,10 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   }
 
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
+
+  LIBC_INLINE constexpr bool is_zero() const {
+    return (bits & EXP_SIG_MASK) == 0;
+  }
 };
 
 } // namespace internal
@@ -121,6 +125,7 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   using UP::TOTAL_LEN;
 
   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.
@@ -175,10 +180,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
     }
   }
 
-  LIBC_INLINE constexpr bool is_zero() const {
-    return (bits & EXP_SIG_MASK) == 0;
-  }
-
   LIBC_INLINE constexpr bool is_inf() const {
     return (bits & EXP_SIG_MASK) == EXP_MASK;
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index cb120f898c2e34..501175b0f6dd59 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -114,10 +114,6 @@ struct FPBits<long double>
     }
   }
 
-  LIBC_INLINE constexpr bool is_zero() const {
-    return (bits & EXP_SIG_MASK) == 0;
-  }
-
   LIBC_INLINE constexpr bool is_inf() const {
     return get_biased_exponent() == MAX_BIASED_EXPONENT &&
            get_mantissa() == 0 && get_implicit_bit() == 1;

>From f1b767dcdf453dd5b1beffbfc39c0ff8a5366916 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 10:35:06 +0000
Subject: [PATCH 10/18] Move get_explicit_exponent to common implementation

---
 libc/src/__support/FPUtil/FPBits.h            | 34 +++++++++----------
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 17 ----------
 2 files changed, 17 insertions(+), 34 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 3ec471f2e9072b..4181772e8ea132 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -85,6 +85,23 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
     return int(get_biased_exponent()) - EXP_BIAS;
   }
 
+  // If the number is subnormal, the exponent is treated as if it were the
+  // minimum exponent for a normal number. This is to keep continuity between
+  // the normal and subnormal ranges, but it causes problems for functions where
+  // values are calculated from the exponent, since just subtracting the bias
+  // 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;
+    }
+  }
+
   LIBC_INLINE constexpr StorageType uintval() const { return bits & FP_MASK; }
 
   LIBC_INLINE constexpr bool is_zero() const {
@@ -163,23 +180,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
 
   LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
 
-  // If the number is subnormal, the exponent is treated as if it were the
-  // minimum exponent for a normal number. This is to keep continuity between
-  // the normal and subnormal ranges, but it causes problems for functions where
-  // values are calculated from the exponent, since just subtracting the bias
-  // 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;
-    }
-  }
-
   LIBC_INLINE constexpr bool is_inf() const {
     return (bits & EXP_SIG_MASK) == EXP_MASK;
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 501175b0f6dd59..1011e61f03fd6b 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -97,23 +97,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  // If the number is subnormal, the exponent is treated as if it were the
-  // minimum exponent for a normal number. This is to keep continuity between
-  // the normal and subnormal ranges, but it causes problems for functions where
-  // values are calculated from the exponent, since just subtracting the bias
-  // 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;
-    }
-  }
-
   LIBC_INLINE constexpr bool is_inf() const {
     return get_biased_exponent() == MAX_BIASED_EXPONENT &&
            get_mantissa() == 0 && get_implicit_bit() == 1;

>From 947db1d2d68717206af4aa9a5c423bccc940b7bb Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 11:05:55 +0000
Subject: [PATCH 11/18] Unify and move is_inf to common implementation

---
 libc/src/__support/FPUtil/FPBits.h            | 19 +++++++++++++++----
 .../__support/FPUtil/x86_64/LongDoubleBits.h  |  5 -----
 2 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 4181772e8ea132..733eb04ca36b1f 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -43,6 +43,13 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   using UP::FRACTION_MASK;
   using UP::SIGN_MASK;
 
+private:
+  // The x86 80 bit float represents the leading digit of the mantissa
+  // explicitly. This is the mask for that bit.
+  LIBC_INLINE_VAR static constexpr StorageType EXPLICIT_BIT_MASK =
+      StorageType(1) << (UP::SIG_LEN - 1);
+
+public:
   // 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.
@@ -107,6 +114,14 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   LIBC_INLINE constexpr bool is_zero() const {
     return (bits & EXP_SIG_MASK) == 0;
   }
+
+  LIBC_INLINE constexpr bool is_inf() const {
+    if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
+      return (bits & EXP_SIG_MASK) == (EXP_MASK | EXPLICIT_BIT_MASK);
+    } else {
+      return (bits & EXP_SIG_MASK) == EXP_MASK;
+    }
+  }
 };
 
 } // namespace internal
@@ -180,10 +195,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
 
   LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
 
-  LIBC_INLINE constexpr bool is_inf() const {
-    return (bits & EXP_SIG_MASK) == EXP_MASK;
-  }
-
   LIBC_INLINE constexpr bool is_nan() const {
     return (bits & EXP_SIG_MASK) > EXP_MASK;
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 1011e61f03fd6b..c07cfd3cb3d2da 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -97,11 +97,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr bool is_inf() const {
-    return get_biased_exponent() == MAX_BIASED_EXPONENT &&
-           get_mantissa() == 0 && get_implicit_bit() == 1;
-  }
-
   LIBC_INLINE constexpr bool is_nan() const {
     if (get_biased_exponent() == MAX_BIASED_EXPONENT) {
       return (get_implicit_bit() == 0) || get_mantissa() != 0;

>From fcdf29cf6b8876522ece78882d20465264951c5c Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:01:51 +0000
Subject: [PATCH 12/18] move is_nan to common implementation

---
 libc/src/__support/FPUtil/FPBits.h            | 24 +++++++++++++++----
 .../__support/FPUtil/x86_64/LongDoubleBits.h  |  9 -------
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 733eb04ca36b1f..b31327a880288e 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -44,10 +44,14 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   using UP::SIGN_MASK;
 
 private:
+  using UP::SIG_MASK;
+
+  LIBC_INLINE_VAR static constexpr StorageType SIG_MSB_BIT_MASK =
+      StorageType(1) << (UP::SIG_LEN - 1);
   // The x86 80 bit float represents the leading digit of the mantissa
   // explicitly. This is the mask for that bit.
   LIBC_INLINE_VAR static constexpr StorageType EXPLICIT_BIT_MASK =
-      StorageType(1) << (UP::SIG_LEN - 1);
+      SIG_MSB_BIT_MASK;
 
 public:
   // Reinterpreting bits as an integer value and interpreting the bits of an
@@ -122,6 +126,20 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
       return (bits & EXP_SIG_MASK) == EXP_MASK;
     }
   }
+
+  LIBC_INLINE constexpr bool is_nan() const {
+    if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
+      const bool has_explicit_bit = bits & EXPLICIT_BIT_MASK;
+      const StorageType exp_bits = bits & EXP_MASK;
+      if (exp_bits == EXP_MASK)
+        return !has_explicit_bit || get_mantissa() != 0;
+      else if (exp_bits)
+        return !has_explicit_bit;
+      return false;
+    } else {
+      return (bits & EXP_SIG_MASK) > EXP_MASK;
+    }
+  }
 };
 
 } // namespace internal
@@ -195,10 +213,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
 
   LIBC_INLINE constexpr explicit operator T() const { return get_val(); }
 
-  LIBC_INLINE constexpr bool is_nan() const {
-    return (bits & EXP_SIG_MASK) > EXP_MASK;
-  }
-
   LIBC_INLINE constexpr bool is_quiet_nan() const {
     return (bits & EXP_SIG_MASK) == (EXP_MASK | QUIET_NAN_MASK);
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index c07cfd3cb3d2da..da08e7b8073a50 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -97,15 +97,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr bool is_nan() const {
-    if (get_biased_exponent() == MAX_BIASED_EXPONENT) {
-      return (get_implicit_bit() == 0) || get_mantissa() != 0;
-    } else if (get_biased_exponent() != 0) {
-      return get_implicit_bit() == 0;
-    }
-    return false;
-  }
-
   LIBC_INLINE constexpr bool is_inf_or_nan() const {
     return (get_biased_exponent() == MAX_BIASED_EXPONENT) ||
            (get_biased_exponent() != 0 && get_implicit_bit() == 0);

>From 55cab492223fd4797116daa07c071d6c3091a247 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:05:25 +0000
Subject: [PATCH 13/18] move is_inf_or_nan() to common implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 15 +++++++++++----
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h |  5 -----
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index b31327a880288e..0bc16fda528870 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -140,6 +140,16 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
       return (bits & EXP_SIG_MASK) > EXP_MASK;
     }
   }
+
+  LIBC_INLINE constexpr bool is_inf_or_nan() const {
+    if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
+      const bool has_explicit_bit = bits & EXPLICIT_BIT_MASK;
+      const StorageType exp_bits = bits & EXP_MASK;
+      return (exp_bits == EXP_MASK) || (exp_bits && !has_explicit_bit);
+    } else {
+      return (bits & EXP_MASK) == EXP_MASK;
+    }
+  }
 };
 
 } // namespace internal
@@ -175,6 +185,7 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   using UP::TOTAL_LEN;
 
   using UP::get_biased_exponent;
+  using UP::is_inf_or_nan;
   using UP::is_zero;
 
   // The function return mantissa with the implicit bit set iff the current
@@ -217,10 +228,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
     return (bits & EXP_SIG_MASK) == (EXP_MASK | QUIET_NAN_MASK);
   }
 
-  LIBC_INLINE constexpr bool is_inf_or_nan() const {
-    return (bits & EXP_MASK) == EXP_MASK;
-  }
-
   LIBC_INLINE constexpr FPBits abs() const {
     return FPBits(bits & EXP_SIG_MASK);
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index da08e7b8073a50..d4d5760e64b566 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -97,11 +97,6 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr bool is_inf_or_nan() const {
-    return (get_biased_exponent() == MAX_BIASED_EXPONENT) ||
-           (get_biased_exponent() != 0 && get_implicit_bit() == 0);
-  }
-
   // Methods below this are used by tests.
 
   LIBC_INLINE static constexpr long double zero() { return 0.0l; }

>From 944117766c28bf9cbb7e9641b49aab2d1e8479b9 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:15:25 +0000
Subject: [PATCH 14/18] move (get|set)_implicit_bit to common implementation

---
 libc/src/__support/FPUtil/FPBits.h                | 12 ++++++++++++
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h |  9 ---------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 0bc16fda528870..155da7119f7e84 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -48,6 +48,7 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
 
   LIBC_INLINE_VAR static constexpr StorageType SIG_MSB_BIT_MASK =
       StorageType(1) << (UP::SIG_LEN - 1);
+
   // The x86 80 bit float represents the leading digit of the mantissa
   // explicitly. This is the mask for that bit.
   LIBC_INLINE_VAR static constexpr StorageType EXPLICIT_BIT_MASK =
@@ -62,6 +63,17 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
   LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
 
+  LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
+    static_assert(UP::ENCODING == FPEncoding::X86_ExtendedPrecision);
+    if (get_implicit_bit() != implicitVal)
+      bits ^= EXPLICIT_BIT_MASK;
+  }
+
+  LIBC_INLINE constexpr bool get_implicit_bit() const {
+    static_assert(UP::ENCODING == FPEncoding::X86_ExtendedPrecision);
+    return bits & EXPLICIT_BIT_MASK;
+  }
+
   LIBC_INLINE constexpr void set_mantissa(StorageType mantVal) {
     mantVal &= FRACTION_MASK;
     bits &= ~FRACTION_MASK;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index d4d5760e64b566..510aa5b89fbc2c 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -65,15 +65,6 @@ struct FPBits<long double>
     return bits & (FRACTION_MASK | EXPLICIT_BIT_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 bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
-  }
-
   LIBC_INLINE constexpr FPBits() : UP() {}
 
   template <typename XType,

>From 0c41ce94cae10be6c945ba3b0222a5c5b1de8d73 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:18:03 +0000
Subject: [PATCH 15/18] Reuse get_implicit_bit function - note this should be
 get_explicit_bit

---
 libc/src/__support/FPUtil/FPBits.h | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 155da7119f7e84..724a62517c52e8 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -63,12 +63,14 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
   LIBC_INLINE constexpr FPBitsCommon() : bits(0) {}
   LIBC_INLINE explicit constexpr FPBitsCommon(StorageType bits) : bits(bits) {}
 
+  // TODO: rename into set_explicit_bit.
   LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
     static_assert(UP::ENCODING == FPEncoding::X86_ExtendedPrecision);
     if (get_implicit_bit() != implicitVal)
       bits ^= EXPLICIT_BIT_MASK;
   }
 
+  // TODO: rename into get_explicit_bit.
   LIBC_INLINE constexpr bool get_implicit_bit() const {
     static_assert(UP::ENCODING == FPEncoding::X86_ExtendedPrecision);
     return bits & EXPLICIT_BIT_MASK;
@@ -141,7 +143,7 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
 
   LIBC_INLINE constexpr bool is_nan() const {
     if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
-      const bool has_explicit_bit = bits & EXPLICIT_BIT_MASK;
+      const bool has_explicit_bit = get_implicit_bit();
       const StorageType exp_bits = bits & EXP_MASK;
       if (exp_bits == EXP_MASK)
         return !has_explicit_bit || get_mantissa() != 0;
@@ -155,9 +157,8 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
 
   LIBC_INLINE constexpr bool is_inf_or_nan() const {
     if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
-      const bool has_explicit_bit = bits & EXPLICIT_BIT_MASK;
       const StorageType exp_bits = bits & EXP_MASK;
-      return (exp_bits == EXP_MASK) || (exp_bits && !has_explicit_bit);
+      return (exp_bits == EXP_MASK) || (exp_bits && !get_implicit_bit());
     } else {
       return (bits & EXP_MASK) == EXP_MASK;
     }

>From d93a45a79320f64107d80f87f67853d3f11c8a7c Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:23:56 +0000
Subject: [PATCH 16/18] move get_explicit_mantissa to common implementation

---
 libc/src/__support/FPUtil/FPBits.h            | 22 +++++++++++--------
 .../__support/FPUtil/x86_64/LongDoubleBits.h  |  7 ------
 2 files changed, 13 insertions(+), 16 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 724a62517c52e8..b822979b18d353 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -86,6 +86,19 @@ template <FPType fp_type> struct FPBitsCommon : private FPProperties<fp_type> {
     return bits & FRACTION_MASK;
   }
 
+  // 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() {
+    if constexpr (UP::ENCODING == FPEncoding::X86_ExtendedPrecision) {
+      return bits & (FRACTION_MASK | EXPLICIT_BIT_MASK);
+    } else {
+      return ((get_biased_exponent() > 0 && !is_inf_or_nan())
+                  ? (FRACTION_MASK + 1)
+                  : 0) |
+             (FRACTION_MASK & bits);
+    }
+  }
+
   LIBC_INLINE constexpr void set_sign(bool signVal) {
     if (get_sign() != signVal)
       bits ^= SIGN_MASK;
@@ -201,15 +214,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   using UP::is_inf_or_nan;
   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);
-  }
-
   static constexpr int MAX_BIASED_EXPONENT = (1 << EXP_LEN) - 1;
   static constexpr StorageType MIN_SUBNORMAL = StorageType(1);
   static constexpr StorageType MAX_SUBNORMAL = FRACTION_MASK;
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 510aa5b89fbc2c..f68d2f406a0e69 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -58,13 +58,6 @@ struct FPBits<long double>
       (StorageType(MAX_BIASED_EXPONENT - 1) << (FRACTION_LEN + 1)) |
       (StorageType(1) << FRACTION_LEN) | MAX_SUBNORMAL;
 
-  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);
-  }
-
   LIBC_INLINE constexpr FPBits() : UP() {}
 
   template <typename XType,

>From 7176349b03ef773433c45e1c53ab732a8bc774cd Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:30:35 +0000
Subject: [PATCH 17/18] Order functions from both files so they are in the same
 order

---
 libc/src/__support/FPUtil/FPBits.h            |  6 ++--
 .../__support/FPUtil/x86_64/LongDoubleBits.h  | 33 ++++++++++---------
 2 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index b822979b18d353..de82c783d43d8e 100644
--- a/libc/src/__support/FPUtil/FPBits.h
+++ b/libc/src/__support/FPUtil/FPBits.h
@@ -221,6 +221,8 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
   static constexpr StorageType MAX_NORMAL =
       ((StorageType(MAX_BIASED_EXPONENT) - 1) << FRACTION_LEN) | MAX_SUBNORMAL;
 
+  LIBC_INLINE constexpr FPBits() : UP() {}
+
   // 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>
@@ -231,8 +233,6 @@ struct FPBits : public internal::FPBitsCommon<get_fp_type<T>()> {
             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() : UP() {}
-
   LIBC_INLINE constexpr void set_val(T value) {
     bits = cpp::bit_cast<StorageType>(value);
   }
@@ -249,6 +249,8 @@ struct FPBits : public internal::FPBitsCommon<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();
   }
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index f68d2f406a0e69..3317d523761195 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -60,6 +60,8 @@ struct FPBits<long double>
 
   LIBC_INLINE constexpr FPBits() : UP() {}
 
+  // 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<long double, XType>, int> = 0>
   LIBC_INLINE constexpr explicit FPBits(XType x)
@@ -73,14 +75,15 @@ struct FPBits<long double>
             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);
-  }
+  // Unimplemented
+  // LIBC_INLINE constexpr void set_val(long double value);
 
   LIBC_INLINE constexpr long double get_val() const {
     return cpp::bit_cast<long double>(bits);
   }
 
+  LIBC_INLINE constexpr operator long double() { return get_val(); }
+
   // Methods below this are used by tests.
 
   LIBC_INLINE static constexpr long double zero() { return 0.0l; }
@@ -99,18 +102,6 @@ struct FPBits<long double>
 
   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();
   }
@@ -127,6 +118,18 @@ struct FPBits<long double>
     return FPBits(MAX_SUBNORMAL).get_val();
   }
 
+  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 FPBits<long double>
   create_value(bool sign, StorageType biased_exp, StorageType mantissa) {
     FPBits<long double> result;

>From 49edb593975ee1a989b4b7807ce9ed1dc40ac5f4 Mon Sep 17 00:00:00 2001
From: Guillaume Chatelet <gchatelet at google.com>
Date: Wed, 20 Dec 2023 13:35:52 +0000
Subject: [PATCH 18/18] make long double conversion explicit as for other
 FPBits

---
 .../FPUtil/generic/sqrt_80_bit_long_double.h         |  2 +-
 libc/src/__support/FPUtil/x86_64/LongDoubleBits.h    |  4 ++--
 .../__support/FPUtil/x86_64/NextAfterLongDouble.h    |  8 ++++----
 libc/test/utils/FPUtil/x86_long_double_test.cpp      | 12 ++++++------
 4 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h
index 257c02e17d0045..8815a18cfbc393 100644
--- a/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h
+++ b/libc/src/__support/FPUtil/generic/sqrt_80_bit_long_double.h
@@ -131,7 +131,7 @@ LIBC_INLINE long double sqrt(long double x) {
     out.set_implicit_bit(1);
     out.set_mantissa((y & (ONE - 1)));
 
-    return out;
+    return out.get_val();
   }
 }
 #endif // LIBC_LONG_DOUBLE_IS_X86_FLOAT80
diff --git a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 3317d523761195..901d4aebca5ab7 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -82,7 +82,7 @@ struct FPBits<long double>
     return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr operator long double() { return get_val(); }
+  LIBC_INLINE constexpr explicit operator long double() { return get_val(); }
 
   // Methods below this are used by tests.
 
@@ -123,7 +123,7 @@ struct FPBits<long double>
     bits.set_biased_exponent(MAX_BIASED_EXPONENT);
     bits.set_implicit_bit(1);
     bits.set_mantissa(v);
-    return bits;
+    return bits.get_val();
   }
 
   LIBC_INLINE static constexpr long double build_quiet_nan(StorageType v) {
diff --git a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
index b461da3a4c0abc..5f15bac5df77f8 100644
--- a/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
+++ b/libc/src/__support/FPUtil/x86_64/NextAfterLongDouble.h
@@ -61,7 +61,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         from_bits.set_biased_exponent(from_bits.get_biased_exponent() + 1);
         if (from_bits.is_inf())
           raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
-        return from_bits;
+        return from_bits.get_val();
       } else {
         ++int_val;
       }
@@ -75,7 +75,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // from == 0 is handled separately so decrementing the exponent will not
         // lead to underflow.
         from_bits.set_biased_exponent(from_bits.get_biased_exponent() - 1);
-        return from_bits;
+        return from_bits.get_val();
       } else {
         --int_val;
       }
@@ -94,7 +94,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         // from == 0 is handled separately so decrementing the exponent will not
         // lead to underflow.
         from_bits.set_biased_exponent(from_bits.get_biased_exponent() - 1);
-        return from_bits;
+        return from_bits.get_val();
       } else {
         --int_val;
       }
@@ -109,7 +109,7 @@ LIBC_INLINE long double nextafter(long double from, long double to) {
         from_bits.set_biased_exponent(from_bits.get_biased_exponent() + 1);
         if (from_bits.is_inf())
           raise_except_if_required(FE_OVERFLOW | FE_INEXACT);
-        return from_bits;
+        return from_bits.get_val();
       } else {
         ++int_val;
       }
diff --git a/libc/test/utils/FPUtil/x86_long_double_test.cpp b/libc/test/utils/FPUtil/x86_long_double_test.cpp
index 7da835fc95fc92..bafbbe2a410759 100644
--- a/libc/test/utils/FPUtil/x86_long_double_test.cpp
+++ b/libc/test/utils/FPUtil/x86_long_double_test.cpp
@@ -27,7 +27,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
     // If exponent has the max value and the implicit bit is 0,
     // then the number is a NaN for all values of mantissa.
     bits.set_mantissa(i);
-    long double nan = bits;
+    long double nan = bits.get_val();
     ASSERT_NE(static_cast<int>(isnan(nan)), 0);
     ASSERT_TRUE(bits.is_nan());
   }
@@ -38,7 +38,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
     // then the number is a NaN for all non-zero values of mantissa.
     // Note the initial value of |i| of 1 to avoid a zero mantissa.
     bits.set_mantissa(i);
-    long double nan = bits;
+    long double nan = bits.get_val();
     ASSERT_NE(static_cast<int>(isnan(nan)), 0);
     ASSERT_TRUE(bits.is_nan());
   }
@@ -49,7 +49,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
     // If exponent is non-zero and also not max, and the implicit bit is 0,
     // then the number is a NaN for all values of mantissa.
     bits.set_mantissa(i);
-    long double nan = bits;
+    long double nan = bits.get_val();
     ASSERT_NE(static_cast<int>(isnan(nan)), 0);
     ASSERT_TRUE(bits.is_nan());
   }
@@ -60,7 +60,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
     // If exponent is non-zero and also not max, and the implicit bit is 1,
     // then the number is normal value for all values of mantissa.
     bits.set_mantissa(i);
-    long double valid = bits;
+    long double valid = bits.get_val();
     ASSERT_EQ(static_cast<int>(isnan(valid)), 0);
     ASSERT_FALSE(bits.is_nan());
   }
@@ -70,7 +70,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
   for (unsigned int i = 0; i < COUNT; ++i) {
     // If exponent is zero, then the number is a valid but denormal value.
     bits.set_mantissa(i);
-    long double valid = bits;
+    long double valid = bits.get_val();
     ASSERT_EQ(static_cast<int>(isnan(valid)), 0);
     ASSERT_FALSE(bits.is_nan());
   }
@@ -80,7 +80,7 @@ TEST(LlvmLibcX86LongDoubleTest, is_nan) {
   for (unsigned int i = 0; i < COUNT; ++i) {
     // If exponent is zero, then the number is a valid but denormal value.
     bits.set_mantissa(i);
-    long double valid = bits;
+    long double valid = bits.get_val();
     ASSERT_EQ(static_cast<int>(isnan(valid)), 0);
     ASSERT_FALSE(bits.is_nan());
   }



More information about the libc-commits mailing list