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

Nick Desaulniers via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 9 09:51:10 PST 2024


================
@@ -582,182 +589,254 @@ 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 < -FRACTION_LEN) {
+      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(FRACTION_LEN + 1)) /
+                               BLOCK_SIZE) -
+                              1;
+      return len = static_cast<uint32_t>(pos_len > 0 ? pos_len : 0);
+    }
     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;
+  FPBits::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 =
+      internal::div_ceil(fputil::FPBits<long double>::MAX_BIASED_EXPONENT -
+                             FPBits::EXP_BIAS,
+                         64) *
+      64;
+  static constexpr size_t EXTRA_INT_WIDTH =
+      internal::div_ceil(sizeof(long double) * 8, 64) * 64;
+
+  // float_as_fixed represents the floating point number as a fixed point number
+  // with the point EXTRA_INT_WIDTH bits from the left of the number. This can
+  // store any number with a negative exponent.
+  cpp::UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH> float_as_fixed = 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::UInt<Bits> &int_num) {
+    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());
+    return static_cast<BlockInt>(wide_result.value());
   }
-}
 
-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::UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH> &int_num) {
+    // WORD_SIZE is the width of the numbers used to internally represent the
+    // UInt
+    for (size_t i = 0; i < EXTRA_INT_WIDTH / int_num.WORD_SIZE; ++i) {
+      int_num[i + (FLOAT_AS_INT_WIDTH / int_num.WORD_SIZE)] = 0;
+    }
+  }
 
-template <>
-LIBC_INLINE constexpr bool FloatToString<long double>::is_lowest_block(size_t) {
-  return false;
-}
+  // init_convert initializes float_as_int, cur_block, and block_buffer based on
+  // the mantissa and exponent of the initial number. Calling it will always
+  // return the class to the starting state.
+  LIBC_INLINE constexpr void init_convert() {
+    // No calculation necessary for the 0 case.
+    if (mantissa == 0 && exponent == 0)
+      return;
+
+    if (exponent > 0) {
+      // if the exponent is positive, then the number is fully above the decimal
+      // point. In this case we represent the float as an integer, then divide
+      // by 10^BLOCK_SIZE and take the remainder as our next block. This
+      // generates the digits from right to left, but the digits will be written
+      // from left to right, so it caches the results so they can be read in
+      // reverse order.
+
+      cpp::UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH> float_as_int = mantissa;
+
+      float_as_int.shift_left(exponent);
+      int_block_index = 0;
+
+      while (float_as_int > 0) {
+        block_buffer[int_block_index] = grab_digits(float_as_int);
+        ++int_block_index;
+      }
+      block_buffer_valid = int_block_index;
+
+    } else {
+      // if the exponent is not positive, then the number is at least partially
+      // below the decimal point. In this case we represent the float as a fixed
+      // point number with the decimal point after the top EXTRA_INT_WIDTH bits.
+      float_as_fixed = mantissa;
+
+      const int SHIFT_AMOUNT = FLOAT_AS_INT_WIDTH + exponent;
+      static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
+      float_as_fixed.shift_left(SHIFT_AMOUNT);
+
+      // If there are still digits above the decimal point, handle those.
+      if (float_as_fixed.clz() < EXTRA_INT_WIDTH) {
+        cpp::UInt<EXTRA_INT_WIDTH> above_decimal_point =
+            float_as_fixed >> FLOAT_AS_INT_WIDTH;
+
+        size_t positive_int_block_index = 0;
+        while (above_decimal_point > 0) {
+          block_buffer[positive_int_block_index] =
+              grab_digits(above_decimal_point);
+          ++positive_int_block_index;
+        }
+        block_buffer_valid = positive_int_block_index;
+
+        // Zero all digits above the decimal point.
+        zero_leading_digits(float_as_fixed);
+        int_block_index = 0;
+      }
+    }
+  }
+
+public:
+  LIBC_INLINE constexpr FloatToString(long double init_float)
+      : float_bits(init_float) {
+    is_negative = float_bits.get_sign();
+    exponent = float_bits.get_explicit_exponent();
+    mantissa = float_bits.get_explicit_mantissa();
 
-template <>
-LIBC_INLINE constexpr BlockInt
-FloatToString<long double>::get_positive_block(int block_index) {
-  if (exponent >= -FRACTION_LEN) {
+    // Adjust for the width of the mantissa.
+    exponent -= FRACTION_LEN;
+
+    this->init_convert();
+  }
+
+  LIBC_INLINE constexpr size_t get_positive_blocks() {
+    if (exponent < -FRACTION_LEN)
+      return 0;
 
-    // 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;
-
-    // shift_amount = -(c0 - exponent) = c_0 + 16 * ceil(exponent/16) - exponent
+    return internal::length_for_num(idx * IDX_SIZE, FRACTION_LEN);
+  }
 
-    cpp::UInt<MID_INT_SIZE> val;
+  LIBC_INLINE constexpr size_t zero_blocks_after_point() {
 #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);
+    return MIN_BLOCK_2[-exponent / IDX_SIZE];
 #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);
-    }
-#endif
-    const uint32_t shift_amount = SHIFT_CONST + pos_exp - exponent;
+    if (exponent >= -FRACTION_LEN)
+      return 0;
 
-    const BlockInt digits =
-        internal::mul_shift_mod_1e9(mantissa, val, (int32_t)(shift_amount));
-    return digits;
-  } else {
-    return 0;
+    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(FRACTION_LEN + 1)) /
+                             BLOCK_SIZE) -
+                            1;
+    return static_cast<uint32_t>(pos_len > 0 ? pos_len : 0);
+#endif
   }
-}
 
-template <>
-LIBC_INLINE constexpr BlockInt
-FloatToString<long double>::get_negative_block(int block_index) {
-  if (exponent < 0) {
-    const int32_t idx = -exponent / IDX_SIZE;
+  LIBC_INLINE constexpr bool is_lowest_block(size_t negative_block_index) {
+    // 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);
 
-    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;
+    return num_requested_digits > -exponent;
+  }
 
-    // if the requested block is zero
-    if (block_index < MIN_BLOCK_2[idx]) {
+  LIBC_INLINE constexpr BlockInt get_positive_block(int block_index) {
+    if (exponent < -FRACTION_LEN)
       return 0;
-    }
-    const uint32_t p = POW10_OFFSET_2[idx] + block_index - MIN_BLOCK_2[idx];
-    // If every digit after the requested block is zero.
-    if (p >= POW10_OFFSET_2[idx + 1]) {
+    if (block_index > static_cast<int>(block_buffer_valid) || block_index < 0)
       return 0;
+
+    return block_buffer[block_index];
+  }
+
+  LIBC_INLINE constexpr BlockInt get_negative_block(int negative_block_index) {
+    if (exponent >= 0)
+      return 0;
+
+    // negative_block_index starts at 0 with the first block after the decimal
+    // point, and 1 with the second and so on. This converts to the same
+    // block_index used everywhere else.
+
+    int block_index = -1 - negative_block_index;
----------------
nickdesaulniers wrote:

Consider any other newly introduced variables if they should be const as well.  You're pretty good about marking things constexpr or const (I'm generally happy to have that qualifier used).  There was perhaps a few places where the qualifiers are missing and look like they could be added.

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


More information about the llvm-commits mailing list