[libc-commits] [llvm] [libc] [libc] Move printf long double to simple calc (PR #75414)

Guillaume Chatelet via libc-commits libc-commits at lists.llvm.org
Thu Dec 21 04:56:48 PST 2023


================
@@ -582,182 +590,259 @@ class FloatToString {
 
   // This takes the index of a block after the decimal point (a negative block)
   // and return if it's sure that all of the digits after it are zero.
-  LIBC_INLINE constexpr bool is_lowest_block(size_t block_index) {
+  LIBC_INLINE constexpr bool is_lowest_block(size_t negative_block_index) {
 #ifdef LIBC_COPT_FLOAT_TO_STR_NO_TABLE
-    return false;
+    // The decimal representation of 2**(-i) will have exactly i digits after
+    // the decimal point.
+    int num_requested_digits =
+        static_cast<int>((negative_block_index + 1) * BLOCK_SIZE);
+
+    return num_requested_digits > -exponent;
 #else
     const int32_t idx = -exponent / IDX_SIZE;
-    const size_t p = POW10_OFFSET_2[idx] + block_index - MIN_BLOCK_2[idx];
+    const size_t p =
+        POW10_OFFSET_2[idx] + negative_block_index - MIN_BLOCK_2[idx];
     // If the remaining digits are all 0, then this is the lowest block.
     return p >= POW10_OFFSET_2[idx + 1];
 #endif
   }
 
   LIBC_INLINE constexpr size_t zero_blocks_after_point() {
 #ifdef LIBC_COPT_FLOAT_TO_STR_NO_TABLE
+    if (exponent < -MANT_WIDTH) {
+      const int pos_exp = -exponent - 1;
+      const uint32_t pos_idx =
+          static_cast<uint32_t>(pos_exp + (IDX_SIZE - 1)) / IDX_SIZE;
+      const int32_t pos_len = ((internal::ceil_log10_pow2(pos_idx * IDX_SIZE) -
+                                internal::ceil_log10_pow2(MANT_WIDTH + 1)) /
+                               BLOCK_SIZE) -
+                              1;
+      const uint32_t len = static_cast<uint32_t>(pos_len > 0 ? pos_len : 0);
+      return len;
+    }
     return 0;
-    // TODO (michaelrj): Find a good algorithm for this that doesn't use a
-    // table.
 #else
     return MIN_BLOCK_2[-exponent / IDX_SIZE];
 #endif
   }
 };
 
-#ifndef LIBC_LONG_DOUBLE_IS_FLOAT64
+#if !defined(LIBC_LONG_DOUBLE_IS_FLOAT64) &&                                   \
+    !defined(LIBC_COPT_FLOAT_TO_STR_NO_SPECIALIZE_LD)
 // --------------------------- LONG DOUBLE FUNCTIONS ---------------------------
 
-template <>
-LIBC_INLINE constexpr size_t FloatToString<long double>::get_positive_blocks() {
-  if (exponent >= -FRACTION_LEN) {
-    const uint32_t idx =
-        exponent < 0
-            ? 0
-            : static_cast<uint32_t>(exponent + (IDX_SIZE - 1)) / IDX_SIZE;
-    const uint32_t len = internal::length_for_num(idx * IDX_SIZE, FRACTION_LEN);
-    return len;
-  } else {
-    return 0;
+template <> class FloatToString<long double> {
+  fputil::FPBits<long double> float_bits;
+  bool is_negative = 0;
+  int exponent = 0;
+  FloatProp::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 size_t FLOAT_AS_INT_WIDTH = 16384;
+  static constexpr size_t FLOAT_AS_INT_WIDTH =
+      internal::div_ceil(fputil::FPBits<long double>::MAX_BIASED_EXPONENT -
+                             FloatProp::EXP_BIAS,
+                         64) *
+      64;
+  // static constexpr size_t EXTRA_INT_WIDTH = 128;
+  static constexpr size_t EXTRA_INT_WIDTH =
+      internal::div_ceil(sizeof(long double) * 8, 64) * 64;
+
+  cpp::BigInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH, false> float_as_int = 0;
+  int int_block_index = 0;
+
+  static constexpr size_t BLOCK_BUFFER_LEN =
+      internal::div_ceil(internal::log10_pow2(FLOAT_AS_INT_WIDTH), BLOCK_SIZE);
+  BlockInt block_buffer[BLOCK_BUFFER_LEN] = {0};
+  size_t block_buffer_valid = 0;
+
+  template <size_t Bits>
+  LIBC_INLINE static constexpr BlockInt
+  grab_digits(cpp::BigInt<Bits, false> &int_num) {
+    BlockInt cur_block = 0;
+    auto wide_result = int_num.div_uint32_times_pow_2(1953125, 9);
+    // the optional only comes into effect when dividing by 0, which will
+    // never happen here. Thus, we just assert that it has value.
+    LIBC_ASSERT(wide_result.has_value());
+    cur_block = static_cast<BlockInt>(wide_result.value());
+    return cur_block;
   }
-}
 
-template <>
-LIBC_INLINE constexpr size_t
-FloatToString<long double>::zero_blocks_after_point() {
-#ifdef LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
-  return MIN_BLOCK_2[-exponent / IDX_SIZE];
-#else
-  return 0;
-  // TODO (michaelrj): Find a good algorithm for this that doesn't use a table.
-#endif
-}
+  LIBC_INLINE static constexpr void zero_leading_digits(
+      cpp::BigInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH, false> &int_num) {
+    // 64 is the width of the numbers used to internally represent the BigInt
+    for (size_t i = 0; i < EXTRA_INT_WIDTH / 64; ++i) {
+      int_num[i + (FLOAT_AS_INT_WIDTH / 64)] = 0;
+    }
+  }
 
-template <>
-LIBC_INLINE constexpr bool FloatToString<long double>::is_lowest_block(size_t) {
-  return false;
-}
+  LIBC_INLINE constexpr void init_convert() {
+    // This initializes float_as_int, cur_block, and block_buffer.
 
-template <>
-LIBC_INLINE constexpr BlockInt
-FloatToString<long double>::get_positive_block(int block_index) {
-  if (exponent >= -FRACTION_LEN) {
+    float_as_int = mantissa;
 
-    // idx is ceil(exponent/16) or 0 if exponent is negative. This is used to
-    // find the coarse section of the POW10_SPLIT table that will be used to
-    // calculate the 9 digit window, as well as some other related values.
-    const uint32_t idx =
-        exponent < 0
-            ? 0
-            : static_cast<uint32_t>(exponent + (IDX_SIZE - 1)) / IDX_SIZE;
-    const uint32_t pos_exp = idx * IDX_SIZE;
+    // No calculation necessary for the 0 case.
+    if (mantissa == 0 && exponent == 0) {
+      return;
+    }
 
-    // shift_amount = -(c0 - exponent) = c_0 + 16 * ceil(exponent/16) - exponent
+    if (exponent > 0) {
+      // if the exponent is positive, then the number is fully above the decimal
+      // point. Shift left by exponent to get the integer representation of this
+      // number.
+      float_as_int.shift_left(exponent);
+      int_block_index = 0;
+
+      while (float_as_int > 0) {
+        BlockInt cur_block = grab_digits(float_as_int);
+        block_buffer[int_block_index] = cur_block;
+        ++int_block_index;
+      }
+      block_buffer_valid = int_block_index;
 
-    cpp::UInt<MID_INT_SIZE> val;
-#ifdef LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
-    // ------------------------------ TABLE MODE -------------------------------
-    const int32_t SHIFT_CONST = TABLE_SHIFT_CONST;
-    val = POW10_SPLIT[POW10_OFFSET[idx] + block_index];
-
-#elif defined(LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT) ||                      \
-    defined(LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT_LD)
-    // ------------------------ DYADIC FLOAT CALC MODE -------------------------
-    const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
-    val = internal::get_table_positive_df<256>(pos_exp, block_index);
-#else
-    // ----------------------------- INT CALC MODE -----------------------------
-    const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
-    const uint64_t MAX_POW_2_SIZE =
-        pos_exp + CALC_SHIFT_CONST - (BLOCK_SIZE * block_index);
-    const uint64_t MAX_POW_5_SIZE =
-        internal::log2_pow5(BLOCK_SIZE * block_index);
-    const uint64_t MAX_INT_SIZE =
-        (MAX_POW_2_SIZE > MAX_POW_5_SIZE) ? MAX_POW_2_SIZE : MAX_POW_5_SIZE;
-
-    if (MAX_INT_SIZE < 1024) {
-      val = internal::get_table_positive<1024>(pos_exp, block_index);
-    } else if (MAX_INT_SIZE < 2048) {
-      val = internal::get_table_positive<2048>(pos_exp, block_index);
-    } else if (MAX_INT_SIZE < 4096) {
-      val = internal::get_table_positive<4096>(pos_exp, block_index);
-    } else if (MAX_INT_SIZE < 8192) {
-      val = internal::get_table_positive<8192>(pos_exp, block_index);
-    } else if (MAX_INT_SIZE < 16384) {
-      val = internal::get_table_positive<16384>(pos_exp, block_index);
     } else {
-      val = internal::get_table_positive<16384 + 128>(pos_exp, block_index);
+      // if the exponent not positive, then the number is at least partially
+      // below the decimal point. Shift left to make the int a fixed point
+      // representation with the decimal point after the top EXTRA_INT_WIDTH
+      // bits.
+      const int SHIFT_AMOUNT = FLOAT_AS_INT_WIDTH + exponent;
+      static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
+      float_as_int.shift_left(SHIFT_AMOUNT);
+
+      // If there are still digits above the decimal point, handle those.
+      if (float_as_int.clz() < EXTRA_INT_WIDTH) {
+        cpp::BigInt<EXTRA_INT_WIDTH, false> above_decimal_point =
----------------
gchatelet wrote:

ditto `UInt` vs `BigInt`

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


More information about the libc-commits mailing list