[libc-commits] [libc] [libc] Add Q length modifier to support float128 conversions in printf (PR #203077)

via libc-commits libc-commits at lists.llvm.org
Wed Jun 10 12:17:40 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libc

Author: Alex Strelnikov (strel-12)

<details>
<summary>Changes</summary>

The spelling follows an existing pattern in e.g. libquadmath for format printing `__float128` values.

A flag is added to disable float128 conversions and the "Q" length modifier.

---

Patch is 52.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203077.diff


13 Files Affected:

- (modified) libc/docs/dev/printf_behavior.rst (+15-1) 
- (modified) libc/src/__support/float_to_string.h (+42-21) 
- (modified) libc/src/stdio/printf_core/converter_utils.h (+4) 
- (modified) libc/src/stdio/printf_core/core_structs.h (+10-7) 
- (modified) libc/src/stdio/printf_core/float_dec_converter.h (+33-19) 
- (modified) libc/src/stdio/printf_core/float_dec_converter_limited.h (+21-8) 
- (modified) libc/src/stdio/printf_core/float_hex_converter.h (+79-55) 
- (modified) libc/src/stdio/printf_core/float_inf_nan_converter.h (+28-20) 
- (modified) libc/src/stdio/printf_core/parser.h (+47-15) 
- (modified) libc/src/stdio/printf_core/printf_config.h (+5) 
- (modified) libc/src/stdio/printf_core/write_int_converter.h (+5) 
- (modified) libc/test/UnitTest/PrintfMatcher.cpp (+1) 
- (modified) libc/test/src/stdio/sprintf_test.cpp (+304) 


``````````diff
diff --git a/libc/docs/dev/printf_behavior.rst b/libc/docs/dev/printf_behavior.rst
index 4b53e0b85f6d2..7d9925eed0acb 100644
--- a/libc/docs/dev/printf_behavior.rst
+++ b/libc/docs/dev/printf_behavior.rst
@@ -105,6 +105,13 @@ with hexadecimal long double conversions (%La). This will improve performance
 significantly, but may cause some tests to fail. This has no effect when float
 conversions are disabled.
 
+LIBC_COPT_PRINTF_NO_CONVERT_FLOAT128
+------------------------------------
+When set, this flag disables support for __float128 conversions using the "Q"
+length modifier (%Qa, %Qf, %Qe, %Qg). This flag has no effect on conversions
+using the "L" length modifier when long double is the same type as __float128.
+This has little to no effect on performance or binary size.
+
 --------------------------------
 Float Conversion Internal Flags:
 --------------------------------
@@ -129,7 +136,7 @@ LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
 -------------------------------------------------
 When set, the float to string decimal conversion algorithm will use a larger
 table to accelerate long double conversions. This larger table is around 5MB of
-size when compiled.
+size when compiled. This flag also affects __float128 conversions.
 
 LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
 ---------------------------------------
@@ -244,3 +251,10 @@ value of errno as an integer with the %d format, including all options. If
 errno = 0 and alt form is specified, the conversion will be a string conversion
 on "0" for simplicity of implementation. This matches what other libcs
 implementing this feature have done.
+
+If the compiler is detected as having support for __float128, "Q" is an accepted
+length modifier for floating point conversions (%Qa, %Qf, %Qe, %Qg), unless
+disabled by LIBC_COPT_PRINTF_NO_CONVERT_FLOAT128. A conversion using the
+"Q" length modifier will be treated as invalid in any of the following
+conditions: __float128 is not supported, the "Q" length modifier is disabled, or
+the conversion does not use a floating point format specifier.
diff --git a/libc/src/__support/float_to_string.h b/libc/src/__support/float_to_string.h
index 683fb75aeda3f..9dce294bc95c6 100644
--- a/libc/src/__support/float_to_string.h
+++ b/libc/src/__support/float_to_string.h
@@ -385,6 +385,14 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
 
 } // namespace internal
 
+#if defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) ||                              \
+    defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE) ||                        \
+    defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
+#define LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
+#endif
+
+template <typename T, typename U = void> class FloatToString {};
+
 // Convert floating point values to their string representation.
 // Because the result may not fit in a reasonably sized array, the caller must
 // request blocks of digits and convert them from integers to strings themself.
@@ -401,8 +409,13 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const FPBits::StorageType mantissa,
 // be zero after the decimal point and before the non-zero digits. Additionally,
 // is_lowest_block will return if the current block is the lowest non-zero
 // block.
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-class FloatToString {
+template <typename T>
+class FloatToString<
+    T, cpp::enable_if_t<cpp::is_same_v<T, float> || cpp::is_same_v<T, double>
+#if defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL)
+                        || cpp_is_same_v<T, long double>
+#endif // LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
+                        >> {
   fputil::FPBits<T> float_bits;
   int exponent;
   FPBits::StorageType mantissa;
@@ -605,36 +618,46 @@ class FloatToString {
   }
 };
 
-#if !defined(LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64) &&                             \
-    !defined(LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE) &&                       \
-    !defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
-// --------------------------- LONG DOUBLE FUNCTIONS ---------------------------
+#if !defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL) ||                    \
+    defined(LIBC_TYPES_HAS_FLOAT128)
+// -------------------- LONG DOUBLE / FLOAT128 FUNCTIONS -----------------------
 
-// this algorithm will work exactly the same for 80 bit and 128 bit long
+// This algorithm will work exactly the same for 80 bit and 128 bit long
 // doubles. They have the same max exponent, but even if they didn't the
 // constants should be calculated to be correct for any provided floating point
 // type.
 
-template <> class FloatToString<long double> {
-  fputil::FPBits<long double> float_bits;
+#if defined(LIBC_TYPES_HAS_FLOAT128)
+template <typename T> struct IsFloat128 : cpp::is_same<T, float128> {};
+#else
+template <typename T> struct IsFloat128 : cpp::bool_constant<false> {};
+#endif
+
+template <typename T>
+class FloatToString<T, cpp::enable_if_t<IsFloat128<T>::value
+#if defined(LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL)
+                                        && !cpp::is_same_v<T, long double>
+#else
+                                        || cpp::is_same_v<T, long double>
+#endif // LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL
+                                        >> {
+  fputil::FPBits<T> float_bits;
   bool is_negative = 0;
   int exponent = 0;
-  fputil::FPBits<long double>::StorageType mantissa = 0;
+  typename fputil::FPBits<T>::StorageType mantissa = 0;
 
-  static constexpr int FRACTION_LEN = fputil::FPBits<long double>::FRACTION_LEN;
-  static constexpr int EXP_BIAS = fputil::FPBits<long double>::EXP_BIAS;
+  static constexpr int FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
+  static constexpr int EXP_BIAS = fputil::FPBits<T>::EXP_BIAS;
   static constexpr size_t UINT_WORD_SIZE = 64;
 
   static constexpr size_t FLOAT_AS_INT_WIDTH =
       internal::div_ceil(
-          fputil::FPBits<long double>::MAX_BIASED_EXPONENT -
-              fputil::FPBits<long double>::EXP_BIAS +
+          fputil::FPBits<T>::MAX_BIASED_EXPONENT - fputil::FPBits<T>::EXP_BIAS +
               FRACTION_LEN, // Add fraction len to provide space for subnormals.
           UINT_WORD_SIZE) *
       UINT_WORD_SIZE;
   static constexpr size_t EXTRA_INT_WIDTH =
-      internal::div_ceil(sizeof(long double) * CHAR_BIT, UINT_WORD_SIZE) *
-      UINT_WORD_SIZE;
+      internal::div_ceil(sizeof(T) * CHAR_BIT, UINT_WORD_SIZE) * UINT_WORD_SIZE;
 
   using wide_int = UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH>;
 
@@ -705,7 +728,7 @@ template <> class FloatToString<long double> {
       // if the shift amount would be negative, then the shift would cause a
       // loss of precision.
       LIBC_ASSERT(SHIFT_AMOUNT >= 0);
-      static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
+      static_assert(EXTRA_INT_WIDTH >= sizeof(T) * 8);
       float_as_fixed <<= SHIFT_AMOUNT;
 
       // If there are still digits above the decimal point, handle those.
@@ -730,8 +753,7 @@ template <> class FloatToString<long double> {
   }
 
 public:
-  LIBC_INLINE constexpr FloatToString(long double init_float)
-      : float_bits(init_float) {
+  LIBC_INLINE constexpr FloatToString(T init_float) : float_bits(init_float) {
     is_negative = float_bits.is_neg();
     exponent = float_bits.get_explicit_exponent();
     mantissa = float_bits.get_explicit_mantissa();
@@ -834,8 +856,7 @@ template <> class FloatToString<long double> {
   }
 };
 
-#endif // !LIBC_TYPES_LONG_DOUBLE_IS_FLOAT64 &&
-       // !LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD
+#endif // !LIBC_INTERNAL_FLOAT_TO_STR_LD_USE_RYU_IMPL || LIBC_TYPES_HAS_FLOAT128
 
 } // namespace LIBC_NAMESPACE_DECL
 
diff --git a/libc/src/stdio/printf_core/converter_utils.h b/libc/src/stdio/printf_core/converter_utils.h
index ed656ffb6e72f..b27defddc354f 100644
--- a/libc/src/stdio/printf_core/converter_utils.h
+++ b/libc/src/stdio/printf_core/converter_utils.h
@@ -57,6 +57,10 @@ LIBC_INLINE uintmax_t apply_length_modifier(uintmax_t num,
     return num & mask;
   }
 #endif // LIBC_COPT_PRINTF_DISABLE_BITINT
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  case LengthModifier::Q: // This case should never happen for integers.
+    return num;
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
   }
   __builtin_unreachable();
 }
diff --git a/libc/src/stdio/printf_core/core_structs.h b/libc/src/stdio/printf_core/core_structs.h
index 705865745db47..9b74a0a391f95 100644
--- a/libc/src/stdio/printf_core/core_structs.h
+++ b/libc/src/stdio/printf_core/core_structs.h
@@ -33,6 +33,9 @@ enum class LengthModifier {
   z,
   t,
   L,
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  Q,
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_COPT_PRINTF_DISABLE_BITINT
   w,
   wf,
@@ -45,6 +48,12 @@ struct LengthSpec {
   size_t bit_width;
 };
 
+// Type large enough to store the raw bits of any floating point type.
+//
+// Does not use any specialization of FPBits, because it is unavailable on
+// PowerPC.
+using AnyFloatStorageType = UInt128;
+
 enum FormatFlags : uint8_t {
   LEFT_JUSTIFIED = 0x01, // -
   FORCE_SIGN = 0x02,     // +
@@ -69,13 +78,7 @@ struct FormatSection {
   int min_width = 0;
   int precision = -1;
 
-  // Needs to be large enough to hold a long double. Special case handling for
-  // the PowerPC double double type because it has no FPBits interface.
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  UInt128 conv_val_raw;
-#else
-  fputil::FPBits<long double>::StorageType conv_val_raw;
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
+  AnyFloatStorageType conv_val_raw;
   void *conv_val_ptr;
 
   char conv_name;
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index 8b99688cb993c..979e2a6011f1f 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -601,16 +601,9 @@ template <typename T, WriteMode write_mode,
 LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  using StorageType = UInt128;
-#else
-  using StorageType = fputil::FPBits<long double>::StorageType;
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-
   // signed because later we use -FRACTION_LEN
   constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
   int exponent = float_bits.get_explicit_exponent();
-  StorageType mantissa = float_bits.get_explicit_mantissa();
 
   char sign_char = 0;
 
@@ -649,7 +642,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
 
   // If the mantissa is 0, then the number is 0, meaning that looping until a
   // non-zero block is found will loop forever. The first block is just 0.
-  if (mantissa != 0) {
+  if (float_bits.get_explicit_mantissa() != 0) {
     // This loop finds the first block.
     while (digits == 0) {
       --cur_block;
@@ -769,16 +762,9 @@ template <typename T, WriteMode write_mode,
 LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
                                              const FormatSection &to_conv,
                                              fputil::FPBits<T> float_bits) {
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  using StorageType = UInt128;
-#else
-  using StorageType = fputil::FPBits<long double>::StorageType;
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-
   // signed because later we use -FRACTION_LEN
   constexpr int32_t FRACTION_LEN = fputil::FPBits<T>::FRACTION_LEN;
   int exponent = float_bits.get_explicit_exponent();
-  StorageType mantissa = float_bits.get_explicit_mantissa();
 
   // From the standard: Let P (init_precision) equal the precision if nonzero, 6
   // if the precision is omitted, or 1 if the precision is zero.
@@ -813,7 +799,7 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
 
   // If the mantissa is 0, then the number is 0, meaning that looping until a
   // non-zero block is found will loop forever.
-  if (mantissa != 0) {
+  if (float_bits.get_explicit_mantissa() != 0) {
     // This loop finds the first non-zero block.
     while (digits == 0) {
       --cur_block;
@@ -1142,8 +1128,17 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
 template <WriteMode write_mode>
 LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  if (to_conv.length_modifier == LengthModifier::Q) {
+    fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
+    fputil::FPBits<float128> float_bits(float_raw);
+    if (!float_bits.is_inf_or_nan()) {
+      return convert_float_decimal_typed<float128>(writer, to_conv, float_bits);
+    }
+  } else
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  if (to_conv.length_modifier == LengthModifier::L) {
+      if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
     fputil::FPBits<long double> float_bits(float_raw);
     if (!float_bits.is_inf_or_nan()) {
@@ -1167,8 +1162,17 @@ LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
 template <WriteMode write_mode>
 LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
                                       const FormatSection &to_conv) {
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  if (to_conv.length_modifier == LengthModifier::Q) {
+    fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
+    fputil::FPBits<float128> float_bits(float_raw);
+    if (!float_bits.is_inf_or_nan()) {
+      return convert_float_dec_exp_typed<float128>(writer, to_conv, float_bits);
+    }
+  } else
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  if (to_conv.length_modifier == LengthModifier::L) {
+      if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
     fputil::FPBits<long double> float_bits(float_raw);
     if (!float_bits.is_inf_or_nan()) {
@@ -1192,8 +1196,18 @@ LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
 template <WriteMode write_mode>
 LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
                                        const FormatSection &to_conv) {
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  if (to_conv.length_modifier == LengthModifier::Q) {
+    fputil::FPBits<float128>::StorageType float_raw = to_conv.conv_val_raw;
+    fputil::FPBits<float128> float_bits(float_raw);
+    if (!float_bits.is_inf_or_nan()) {
+      return convert_float_dec_auto_typed<float128>(writer, to_conv,
+                                                    float_bits);
+    }
+  } else
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  if (to_conv.length_modifier == LengthModifier::L) {
+      if (to_conv.length_modifier == LengthModifier::L) {
     fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
     fputil::FPBits<long double> float_bits(float_raw);
     if (!float_bits.is_inf_or_nan()) {
diff --git a/libc/src/stdio/printf_core/float_dec_converter_limited.h b/libc/src/stdio/printf_core/float_dec_converter_limited.h
index 637369d2f8eda..2693ebd531f96 100644
--- a/libc/src/stdio/printf_core/float_dec_converter_limited.h
+++ b/libc/src/stdio/printf_core/float_dec_converter_limited.h
@@ -78,7 +78,8 @@ struct DigitsInput {
   // Constructor which accepts a mantissa direct from a floating-point format,
   // and shifts it up to the top of the UInt128 so that a function consuming
   // this struct afterwards doesn't have to remember which format it came from.
-  DigitsInput(int32_t fraction_len, UInt128 mantissa_, int exponent_, Sign sign)
+  DigitsInput(int32_t fraction_len, AnyFloatStorageType mantissa_,
+              int exponent_, Sign sign)
       : mantissa(UInt128(mantissa_) << (127 - fraction_len)),
         exponent(exponent_), sign(sign) {
     if (!(mantissa & (UInt128(1) << 127)) && mantissa != 0) {
@@ -374,10 +375,11 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
 }
 
 template <WriteMode write_mode>
-LIBC_INLINE int
-convert_float_inner(Writer<write_mode> *writer, const FormatSection &to_conv,
-                    int32_t fraction_len, int exponent, UInt128 mantissa,
-                    Sign sign, ConversionType ctype) {
+LIBC_INLINE int convert_float_inner(Writer<write_mode> *writer,
+                                    const FormatSection &to_conv,
+                                    int32_t fraction_len, int exponent,
+                                    AnyFloatStorageType mantissa, Sign sign,
+                                    ConversionType ctype) {
   constexpr char DECIMAL_POINT = '.';
   // If to_conv doesn't specify a precision, the precision defaults to 6.
   unsigned precision = to_conv.precision < 0 ? 6 : to_conv.precision;
@@ -631,10 +633,21 @@ template <WriteMode write_mode>
 LIBC_INLINE int convert_float_outer(Writer<write_mode> *writer,
                                     const FormatSection &to_conv,
                                     ConversionType ctype) {
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  if (to_conv.length_modifier == LengthModifier::Q) {
+    fputil::FPBits<float128> float_bits(
+        static_cast<fputil::FPBits<float128>::StorageType>(
+            to_conv.conv_val_raw));
+    if (!float_bits.is_inf_or_nan()) {
+      return convert_float_typed<float128>(writer, to_conv, float_bits, ctype);
+    }
+  } else
+#endif // LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128
 #ifndef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  if (to_conv.length_modifier == LengthModifier::L) {
-    fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
-    fputil::FPBits<long double> float_bits(float_raw);
+      if (to_conv.length_modifier == LengthModifier::L) {
+    fputil::FPBits<long double> float_bits(
+        static_cast<fputil::FPBits<long double>::StorageType>(
+            to_conv.conv_val_raw));
     if (!float_bits.is_inf_or_nan()) {
       return convert_float_typed<long double>(writer, to_conv, float_bits,
                                               ctype);
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index 5c6899156ccba..8f37c1bfe1c50 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -25,52 +25,72 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
-                                      const FormatSection &to_conv) {
-#ifdef LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-  using LDBits = fputil::FPBits<double>;
-  using StorageType = LDBits::StorageType;
-#else
-  using LDBits = fputil::FPBits<long double>;
-  using StorageType = LDBits::StorageType;
-#endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
-
+struct FloatHexExpFPBitsProperties {
   bool is_negative;
   int exponent;
-  StorageType mantissa;
+  AnyFloatStorageType mantissa;
   bool is_inf_or_nan;
   uint32_t fraction_bits;
+};
+
+template <typename T>
+FloatHexExpFPBitsProperties
+get_float_hex_exp_fp_bits_properties(AnyFloatStorageType float_raw) {
+  fputil::FPBits<T> float_bits(
+      static_cast<typename fputil::FPBits<T>::StorageType>(float_raw));
+  return {
+      .is_negative = float_bits.is_neg(),
+      .exponent = float_bits.get_explicit_exponent(),
+      .mantissa = float_bits.get_explicit_mantissa(),
+      .is_inf_or_nan = float_bits.is_inf_or_nan(),
+      .fraction_bits = fputil::FPBits<T>::FRACTION_LEN,
+  };
+}
 
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
+                                      const FormatSection &to_conv) {
+#if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
+  static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
+      fputil::FPBits<float...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/203077


More information about the libc-commits mailing list