diff --git a/libc/src/__support/FPUtil/FPBits.h b/libc/src/__support/FPUtil/FPBits.h index 8304b76d3d8a..63eeba85f15e 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 { 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 { @@ -57,7 +50,6 @@ template <> struct FPLayout { 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 { @@ -65,7 +57,6 @@ template <> struct FPLayout { 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 { @@ -73,7 +64,6 @@ template <> struct FPLayout { 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 { @@ -81,15 +71,13 @@ template <> struct FPLayout { 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 -struct FPBaseMasksAndShifts : public internal::FPLayout { +struct FPRepBase : public internal::FPLayout { private: using UP = internal::FPLayout; @@ -149,73 +137,44 @@ private: 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(); -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. -template struct FPRep : private FPBaseMasksAndShifts { - using UP = FPBaseMasksAndShifts; - 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 @@ public: 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 @@ public: } }; +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 struct FPRep : public FPRepBase { + using UP = FPRepBase; + 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 struct FPBits : public internal::FPRep()> { static_assert(cpp::is_floating_point_v, "FPBits instantiated with invalid type."); using UP = internal::FPRep()>; - 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,9 +306,37 @@ public: using UP::FRACTION_MASK; using UP::SIGN_MASK; using UP::TOTAL_LEN; + using UP::UP; using UP::get_biased_exponent; using UP::is_zero; + // 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) << SIG_LEN) | SIG_MASK; + + // Constructors. + LIBC_INLINE constexpr FPBits() = default; + + template LIBC_INLINE constexpr explicit FPBits(XType x) { + using Unqual = typename cpp::remove_cv_t; + if constexpr (cpp::is_same_v) { + bits = cpp::bit_cast(x); + } else if constexpr (cpp::is_same_v) { + bits = x; + } else { + // We don't want accidental type promotions/conversions, so we require + // exact type match. + static_assert(cpp::always_false); + } + } + // Floating-point conversions. + LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast(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. @@ -340,33 +347,6 @@ public: (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; - 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 , int> = 0> - LIBC_INLINE constexpr explicit FPBits(XType x) - : UP(cpp::bit_cast(x)) {} - - template , 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(value); - } - - LIBC_INLINE constexpr T get_val() const { return cpp::bit_cast(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; } @@ -387,14 +367,22 @@ public: 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 @@ public: } LIBC_INLINE static constexpr T build_nan(StorageType v) { - FPBits 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 + 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 @@ public: } return result; } - - LIBC_INLINE static constexpr FPBits - create_value(bool sign, StorageType biased_exp, StorageType mantissa) { - FPBits 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 ce368c89f95e..50019f32b2c4 100644 --- a/libc/src/__support/FPUtil/fpbits_str.h +++ b/libc/src/__support/FPUtil/fpbits_str.h @@ -45,7 +45,7 @@ template LIBC_INLINE cpp::string str(fputil::FPBits x) { cpp::string s; - const details::ZeroPaddedHexFmt bits(x.bits); + const details::ZeroPaddedHexFmt 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 4dc5d25e2698..8abc0c87af0d 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 : public internal::FPRep { using UP = internal::FPRep; 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; + (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); + // Constructors. + LIBC_INLINE constexpr FPBits() = default; + + template LIBC_INLINE constexpr explicit FPBits(XType x) { + using Unqual = typename cpp::remove_cv_t; + if constexpr (cpp::is_same_v) { + bits = cpp::bit_cast(x); + } else if constexpr (cpp::is_same_v) { + bits = x; + } else { + // We don't want accidental type promotions/conversions, so we require + // exact type match. + static_assert(cpp::always_false); + } } - 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(bits); + } + + LIBC_INLINE constexpr operator long double() const { + return cpp::bit_cast(bits); + } + + LIBC_INLINE constexpr StorageType get_explicit_mantissa() const { + return bits & SIG_MASK; } LIBC_INLINE constexpr bool get_implicit_bit() const { - return bool((bits & (StorageType(1) << FRACTION_LEN)) >> FRACTION_LEN); + return bits & EXPLICIT_BIT_MASK; } - LIBC_INLINE constexpr FPBits() : UP() {} - - template , int> = 0> - LIBC_INLINE constexpr explicit FPBits(XType x) - : UP(cpp::bit_cast(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 , int> = 0> - LIBC_INLINE constexpr explicit FPBits(XType x) : UP(x) {} - - LIBC_INLINE constexpr operator long double() { - return cpp::bit_cast(bits); - } - - LIBC_INLINE constexpr long double get_val() const { - return cpp::bit_cast(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 @@ public: // 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 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 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 @@ public: return FPBits(MAX_SUBNORMAL).get_val(); } - LIBC_INLINE static constexpr FPBits - create_value(bool sign, StorageType biased_exp, StorageType mantissa) { - FPBits 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 f82683bcc055..8f22fdea9321 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 d2b94f22687f..df82f24dc696 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 b70183958899..da69f7f5ad4d 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 a333a075fe5a..1427d1934db9 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 e7aeda723b50..07dedba85e62 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 2ebdddbb2d16..f1f93468479b 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(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 37db385c959b..86ac8a6e26d7 100644 --- a/libc/test/src/stdlib/strtold_test.cpp +++ b/libc/test/src/stdlib/strtold_test.cpp @@ -87,7 +87,7 @@ public: 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 187df4add1e7..9c3e8d3f30f2 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( static_cast(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());