[libc-commits] [libc] [llvm] [libc] Move printf long double to simple calc (PR #75414)
Nick Desaulniers via libc-commits
libc-commits at lists.llvm.org
Fri Jan 5 09:02:13 PST 2024
================
@@ -582,182 +590,269 @@ 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;
+ 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 =
+ internal::div_ceil(fputil::FPBits<long double>::MAX_BIASED_EXPONENT -
+ FloatProp::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) {
+ 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::UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH> &int_num) {
+ // 64 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;
+ }
-template <>
-LIBC_INLINE constexpr BlockInt
-FloatToString<long double>::get_positive_block(int block_index) {
- if (exponent >= -FRACTION_LEN) {
+ 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.
- // 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;
+ cpp::UInt<FLOAT_AS_INT_WIDTH + EXTRA_INT_WIDTH> float_as_int = mantissa;
- // shift_amount = -(c0 - exponent) = c_0 + 16 * ceil(exponent/16) - exponent
+ 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 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) {
+ BlockInt cur_block = grab_digits(above_decimal_point);
+ block_buffer[positive_int_block_index] = cur_block;
+ ++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;
+ }
}
-#endif
- const uint32_t shift_amount = SHIFT_CONST + pos_exp - exponent;
+ }
- const BlockInt digits =
- internal::mul_shift_mod_1e9(mantissa, val, (int32_t)(shift_amount));
- return digits;
- } else {
- return 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();
+
+ // Adjust for the width of the mantissa.
+ exponent -= FRACTION_LEN;
+
+ this->init_convert();
}
-}
-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 size_t 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;
+ }
+ }
- 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;
+ return MIN_BLOCK_2[-exponent / IDX_SIZE];
+#else
+ 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;
+ const uint32_t len = static_cast<uint32_t>(pos_len > 0 ? pos_len : 0);
+ return len;
+ }
+ return 0;
----------------
nickdesaulniers wrote:
invert condition + return early
https://github.com/llvm/llvm-project/pull/75414
More information about the libc-commits
mailing list