[libc-commits] [libc] d02471e - [libc][NFC] Simplify `FPBits` (#76835)

via libc-commits libc-commits at lists.llvm.org
Thu Jan 4 07:14:32 PST 2024


Author: Guillaume Chatelet
Date: 2024-01-04T16:14:28+01:00
New Revision: d02471ede56e727d7d16f61bcdecc4f99a2bda02

URL: https://github.com/llvm/llvm-project/commit/d02471ede56e727d7d16f61bcdecc4f99a2bda02
DIFF: https://github.com/llvm/llvm-project/commit/d02471ede56e727d7d16f61bcdecc4f99a2bda02.diff

LOG: [libc][NFC] Simplify `FPBits` (#76835)

This patch reduces the scope of `FPBits` exported variables and
functions.
It also moves storage up into `FPRep` and tries to make the default and
specialized versions of `FPBits` more uniform.

The next step is to move the specialization from `FPBits` to `FPRep` so
we can manipulate floating point representations through `FPType` 
alone - that is - independently from the host architecture.

Added: 
    

Modified: 
    libc/src/__support/FPUtil/FPBits.h
    libc/src/__support/FPUtil/fpbits_str.h
    libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
    libc/src/math/generic/log.cpp
    libc/src/math/generic/log10.cpp
    libc/src/math/generic/log10f.cpp
    libc/src/math/generic/log2.cpp
    libc/src/math/generic/log2f.cpp
    libc/src/math/generic/logf.cpp
    libc/test/src/stdlib/strtold_test.cpp
    libc/test/src/time/difftime_test.cpp

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h
index 8304b76d3d8ad0..63eeba85f15e5c 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,15 +71,13 @@ 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
 
-// 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,73 +137,44 @@ struct FPBaseMasksAndShifts : public internal::FPLayout<fp_type> {
     return StorageType(1) << position;
   }
 
-public:
+  // Merge bits from 'a' and 'b' values according to 'mask'.
+  // Use 'a' bits when corresponding 'mask' bits are zeroes and 'b' bits when
+  // corresponding bits are ones.
+  LIBC_INLINE static constexpr StorageType merge(StorageType a, StorageType b,
+                                                 StorageType mask) {
+    // https://graphics.stanford.edu/~seander/bithacks.html#MaskedMerge
+    return a ^ ((a ^ b) & mask);
+  }
+
+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 =
       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 =
-      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...
-};
-
-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;
-    bits |= mantVal;
-  }
-
-  LIBC_INLINE constexpr StorageType get_mantissa() const {
-    return bits & FRACTION_MASK;
+  LIBC_INLINE constexpr bool get_sign() const {
+    return (bits & SIGN_MASK) != 0;
   }
 
   LIBC_INLINE constexpr void set_sign(bool signVal) {
@@ -223,21 +182,22 @@ template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
       bits ^= SIGN_MASK;
   }
 
-  LIBC_INLINE constexpr bool get_sign() const {
-    return (bits & SIGN_MASK) != 0;
+  LIBC_INLINE constexpr StorageType get_mantissa() const {
+    return bits & FRACTION_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 void set_mantissa(StorageType mantVal) {
+    bits = merge(bits, mantVal, FRACTION_MASK);
   }
 
   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) {
+    bits = merge(bits, biased << EXP_MASK_SHIFT, EXP_MASK);
+  }
+
   LIBC_INLINE constexpr int get_exponent() const {
     return int(get_biased_exponent()) - EXP_BIAS;
   }
@@ -266,6 +226,23 @@ template <FPType fp_type> struct FPRep : private FPBaseMasksAndShifts<fp_type> {
   }
 };
 
+namespace internal {
+
+// 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::FRACTION_LEN;
+  using UP::FRACTION_MASK;
+  using UP::MANTISSA_PRECISION;
+};
+
 } // namespace internal
 
 // Returns the FPType corresponding to C++ type T on the host.
@@ -311,14 +288,16 @@ 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;
+  using UP::SIG_LEN;
+  using UP::SIG_MASK;
 
 public:
+  using StorageType = typename UP::StorageType;
+  using UP::bits;
   using UP::EXP_BIAS;
   using UP::EXP_LEN;
   using UP::EXP_MASK;
@@ -327,46 +306,47 @@ 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;
-
-  // 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;
   static constexpr StorageType MIN_NORMAL = (StorageType(1) << FRACTION_LEN);
   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)) {}
+      (StorageType(MAX_BIASED_EXPONENT - 1) << SIG_LEN) | SIG_MASK;
 
-  template <typename XType,
-            cpp::enable_if_t<cpp::is_same_v<XType, StorageType>, int> = 0>
-  LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {}
+  // Constructors.
+  LIBC_INLINE constexpr FPBits() = default;
 
-  LIBC_INLINE constexpr FPBits() : UP() {}
-
-  LIBC_INLINE constexpr void set_val(T value) {
-    bits = cpp::bit_cast<StorageType>(value);
+  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 {
+      // We don't want accidental type promotions/conversions, so we require
+      // exact type match.
+      static_assert(cpp::always_false<XType>);
+    }
   }
-
+  // Floating-point conversions.
   LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast<T>(bits); }
 
   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.
+  LIBC_INLINE constexpr StorageType get_explicit_mantissa() {
+    return ((get_biased_exponent() > 0 && !is_inf_or_nan())
+                ? (FRACTION_MASK + 1)
+                : 0) |
+           (FRACTION_MASK & bits);
+  }
+
   LIBC_INLINE constexpr bool is_inf() const {
     return (bits & EXP_SIG_MASK) == EXP_MASK;
   }
@@ -387,14 +367,22 @@ 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)) // sign
+                      | 0                                 // exponent
+                      | 0;                                // mantissa
+    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); }
@@ -416,15 +404,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)
@@ -452,15 +449,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/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/src/__support/FPUtil/x86_64/LongDoubleBits.h b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
index 4dc5d25e269820..8abc0c87af0d20 100644
--- a/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
+++ b/libc/src/__support/FPUtil/x86_64/LongDoubleBits.h
@@ -30,70 +30,69 @@ 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;
+  // 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.
+  static constexpr StorageType EXPLICIT_BIT_MASK = StorageType(1)
+                                                   << FRACTION_LEN;
+  // 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 =
-      (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;
-
-  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);
+      (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) {
+    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 {
+      // We don't want accidental type promotions/conversions, so we require
+      // exact type match.
+      static_assert(cpp::always_false<XType>);
+    }
   }
 
-  LIBC_INLINE constexpr void set_implicit_bit(bool implicitVal) {
-    bits &= ~(StorageType(1) << FRACTION_LEN);
-    bits |= (StorageType(implicitVal) << FRACTION_LEN);
+  // Floating-point conversions.
+  LIBC_INLINE constexpr long double get_val() const {
+    return cpp::bit_cast<long double>(bits);
   }
 
-  LIBC_INLINE constexpr bool get_implicit_bit() const {
-    return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN);
+  LIBC_INLINE constexpr operator long double() const {
+    return cpp::bit_cast<long double>(bits);
   }
 
-  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)
-      : 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);
+  LIBC_INLINE constexpr StorageType get_explicit_mantissa() const {
+    return bits & SIG_MASK;
   }
 
-  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);
+  LIBC_INLINE constexpr bool get_implicit_bit() const {
+    return bits & EXPLICIT_BIT_MASK;
   }
 
-  LIBC_INLINE constexpr long double get_val() const {
-    return cpp::bit_cast<long double>(bits);
+  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 {
@@ -117,34 +116,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();
   }
@@ -161,13 +152,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);
   }
 };
 

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();
     }

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/
diff time_test.cpp b/libc/test/src/time/
diff time_test.cpp
index 187df4add1e727..9c3e8d3f30f2b9 100644
--- a/libc/test/src/time/
diff time_test.cpp
+++ b/libc/test/src/time/
diff time_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());


        


More information about the libc-commits mailing list