[libc] add options to printf decimal floats

This patch adds three options for printf decimal long doubles, and these
can also apply to doubles.

1. Use a giant table which is fast and accurate, but takes up ~5MB).
2. Use dyadic floats for approximations, which only gives ~50 digits of
   accuracy but is very fast.
3. Use large integers for approximations, which is accurate but very
   slow.

Reviewed By: sivachandra, lntue

Differential Revision: https://reviews.llvm.org/D150399
This commit is contained in:
Michael Jones 2023-03-28 11:18:08 -07:00
parent 086183c6c6
commit 688b9730d1
11 changed files with 871 additions and 99 deletions

View File

@ -102,6 +102,7 @@ add_header_library(
HDRS
float_to_string.h
ryu_constants.h
ryu_long_double_constants.h
DEPENDS
.libc_assert
libc.src.__support.CPP.type_traits

View File

@ -52,14 +52,14 @@ template <size_t Bits> struct DyadicFloat {
normalize();
}
DyadicFloat(bool s, int e, MantissaType m)
constexpr DyadicFloat(bool s, int e, MantissaType m)
: sign(s), exponent(e), mantissa(m) {
normalize();
}
// Normalizing the mantissa, bringing the leading 1 bit to the most
// significant bit.
DyadicFloat &normalize() {
constexpr DyadicFloat &normalize() {
if (!mantissa.is_zero()) {
int shift_length = static_cast<int>(mantissa.clz());
exponent -= shift_length;
@ -118,6 +118,24 @@ template <size_t Bits> struct DyadicFloat {
// Still correct without FMA instructions if `d_lo` is not underflow.
return multiply_add(d_lo.get_val(), T(round_and_sticky), d_hi.get_val());
}
explicit operator MantissaType() const {
if (mantissa.is_zero())
return 0;
MantissaType new_mant = mantissa;
if (exponent > 0) {
new_mant <<= exponent;
} else {
new_mant >>= (-exponent);
}
if (sign) {
new_mant = (~new_mant) + 1;
}
return new_mant;
}
};
// Quick add - Add 2 dyadic floats with rounding toward 0 and then normalize the
@ -208,6 +226,30 @@ constexpr DyadicFloat<Bits> quick_mul(DyadicFloat<Bits> a,
return result;
}
// Simple exponentiation implementation for printf. Only handles positive
// exponents, since division isn't implemented.
template <size_t Bits>
constexpr DyadicFloat<Bits> pow_n(DyadicFloat<Bits> a, uint32_t power) {
DyadicFloat<Bits> result = 1.0;
DyadicFloat<Bits> cur_power = a;
while (power > 0) {
if ((power % 2) > 0) {
result = quick_mul(result, cur_power);
}
power = power >> 1;
cur_power = quick_mul(cur_power, cur_power);
}
return result;
}
template <size_t Bits>
constexpr DyadicFloat<Bits> mul_pow_2(DyadicFloat<Bits> a, int32_t pow_2) {
DyadicFloat<Bits> result = a;
result.exponent += pow_2;
return result;
}
} // namespace __llvm_libc::fputil
#endif // LLVM_LIBC_SRC_SUPPORT_FPUTIL_DYADIC_FLOAT_H

View File

@ -13,10 +13,65 @@
#include "src/__support/CPP/type_traits.h"
#include "src/__support/FPUtil/FPBits.h"
#include "src/__support/FPUtil/dyadic_float.h"
#include "src/__support/UInt.h"
#include "src/__support/common.h"
#include "src/__support/libc_assert.h"
// This file has 5 compile-time flags to allow the user to configure the float
// to string behavior. These allow the user to select which 2 of the 3 useful
// properties they want. The useful properties are:
// 1) Speed of Evaluation
// 2) Small Size of Binary
// 3) Centered Output Value
// These are explained below with the flags that are missing each one.
// LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
// The Mega Table is ~5 megabytes when compiled. It lists the constants needed
// to perform the Ryu Printf algorithm (described below) for all long double
// values. This makes it extremely fast for both doubles and long doubles, in
// exchange for large binary size.
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT_LD
// Dyadic floats are software floating point numbers, and their accuracy can be
// as high as necessary. This option uses 256 bit dyadic floats to calculate
// the table values that Ryu Printf needs. This is reasonably fast and very
// small compared to the Mega Table, but the 256 bit floats only give accurate
// results for the first ~50 digits of the output. In practice this shouldn't
// be a problem since long doubles are only accurate for ~35 digits, but the
// trailing values all being 0s may cause brittle tests to fail. The _LD
// version of this flag only effects the long double calculations, and the
// other version effects both long double and double.
// LIBC_COPT_FLOAT_TO_STR_USE_INT_CALC
// Integer Calculation uses wide integers to do the calculations for the Ryu
// Printf table, which is just as accurate as the Mega Table without requiring
// as much code size. These integers can be very large (~32KB at max, though
// always on the stack) to handle the edges of the long double range. They are
// also very slow, taking multiple seconds on a powerful CPU to calculate the
// values at the end of the range. If no flag is set, this is used for long
// doubles, the flag only changes the double behavior.
// LIBC_COPT_FLOAT_TO_STR_NO_TABLE
// This flag doesn't change the actual calculation method, instead it is used
// to disable the normal Ryu Printf table for configurations that don't use any
// table at all.
// Default Config:
// If no flags are set, doubles use the normal (and much more reasonably sized)
// Ryu Printf table and long doubles use Integer Calculation. This is because
// long doubles are rarely used and the normal Ryu Printf table is very fast
// for doubles.
#ifdef LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
#include "src/__support/ryu_long_double_constants.h"
#elif !defined(LIBC_COPT_FLOAT_TO_STR_NO_TABLE)
#include "src/__support/ryu_constants.h"
#else
constexpr size_t IDX_SIZE = 1;
constexpr size_t MID_INT_SIZE = 192;
#endif
// This implementation is based on the Ryu Printf algorithm by Ulf Adams:
// Ulf Adams. 2019. Ryū revisited: printf floating point conversion.
@ -52,20 +107,41 @@ constexpr size_t BLOCK_SIZE = 9;
using MantissaInt = fputil::FPBits<long double>::UIntType;
constexpr size_t POW10_ADDITIONAL_BITS_CALC = 128;
constexpr size_t POW10_ADDITIONAL_BITS_TABLE = 120;
constexpr size_t MID_INT_SIZE = 192;
// Larger numbers prefer a slightly larger constant than is used for the smaller
// numbers.
constexpr size_t CALC_SHIFT_CONST = 128;
namespace internal {
// Returns floor(log_10(2^e)); requires 0 <= e <= 1650.
LIBC_INLINE constexpr uint32_t log10_pow2(const uint32_t e) {
// The first value this approximation fails for is 2^1651 which is just
// greater than 10^297.
LIBC_ASSERT(e >= 0 && e <= 1650 &&
// Returns floor(log_10(2^e)); requires 0 <= e <= 42039.
LIBC_INLINE constexpr uint32_t log10_pow2(const uint64_t e) {
LIBC_ASSERT(e >= 0 && e <= 42039 &&
"Incorrect exponent to perform log10_pow2 approximation.");
return (e * 78913) >> 18;
// This approximation is based on the float value for log_10(2). It first
// gives an incorrect result for our purposes at 42039 (well beyond the 16383
// maximum for long doubles).
// To get these constants I first evaluated log_10(2) to get an approximation
// of 0.301029996. Next I passed that value through a string to double
// conversion to get an explicit mantissa of 0x13441350fbd738 and an exponent
// of -2 (which becomes -54 when we shift the mantissa to be a non-fractional
// number). Next I shifted the mantissa right 12 bits to create more space for
// the multiplication result, adding 12 to the exponent to compensate. To
// check that this approximation works for our purposes I used the following
// python code:
// for i in range(16384):
// if(len(str(2**i)) != (((i*0x13441350fbd)>>42)+1)):
// print(i)
// The reason we add 1 is because this evaluation truncates the result, giving
// us the floor, whereas counting the digits of the power of 2 gives us the
// ceiling. With a similar loop I checked the maximum valid value and found
// 42039.
return (e * 0x13441350fbdll) >> 42;
}
// Same as above, but with different constants.
LIBC_INLINE constexpr uint32_t log2_pow5(const uint64_t e) {
return (e * 0x12934f0979bll) >> 39;
}
// Returns 1 + floor(log_10(2^e). This could technically be off by 1 if any
@ -81,7 +157,7 @@ LIBC_INLINE constexpr uint32_t ceil_log10_pow2(const uint32_t e) {
LIBC_INLINE constexpr uint32_t length_for_num(const uint32_t idx,
const uint32_t mantissa_width) {
//+8 to round up when dividing by 9
return (ceil_log10_pow2(16 * idx) + ceil_log10_pow2(mantissa_width) +
return (ceil_log10_pow2(idx) + ceil_log10_pow2(mantissa_width) +
(BLOCK_SIZE - 1)) /
BLOCK_SIZE;
// return (ceil_log10_pow2(16 * idx + mantissa_width) + 8) / 9;
@ -95,14 +171,14 @@ LIBC_INLINE constexpr uint32_t length_for_num(const uint32_t idx,
// TODO: Fix long doubles (needs bigger table or alternate algorithm.)
// Currently the table values are generated, which is very slow.
template <size_t INT_SIZE>
LIBC_INLINE constexpr cpp::UInt<MID_INT_SIZE>
get_table_positive(int exponent, size_t i, const size_t constant) {
LIBC_INLINE constexpr cpp::UInt<MID_INT_SIZE> get_table_positive(int exponent,
size_t i) {
// INT_SIZE is the size of int that is used for the internal calculations of
// this function. It should be large enough to hold 2^(exponent+constant), so
// ~1000 for double and ~16000 for long double. Be warned that the time
// complexity of exponentiation is O(n^2 * log_2(m)) where n is the number of
// bits in the number being exponentiated and m is the exponent.
int shift_amount = exponent + constant - (9 * i);
const int shift_amount = exponent + CALC_SHIFT_CONST - (BLOCK_SIZE * i);
if (shift_amount < 0) {
return 1;
}
@ -110,8 +186,10 @@ get_table_positive(int exponent, size_t i, const size_t constant) {
// MOD_SIZE is one of the limiting factors for how big the constant argument
// can get, since it needs to be small enough to fit in the result UInt,
// otherwise we'll get truncation on return.
const cpp::UInt<INT_SIZE> MOD_SIZE =
(cpp::UInt<INT_SIZE>(1) << constant) * 1000000000;
constexpr cpp::UInt<INT_SIZE> MOD_SIZE =
(cpp::UInt<INT_SIZE>(1000000000)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));
constexpr uint64_t FIVE_EXP_NINE = 1953125;
num = cpp::UInt<INT_SIZE>(1) << (shift_amount);
@ -123,11 +201,64 @@ get_table_positive(int exponent, size_t i, const size_t constant) {
num = num + 1;
if (num > MOD_SIZE) {
num = num % MOD_SIZE;
auto rem =
num.div_uint32_times_pow_2(
1000000000, CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0))
.value();
num = rem;
}
return num;
}
template <size_t INT_SIZE>
LIBC_INLINE cpp::UInt<MID_INT_SIZE> get_table_positive_df(int exponent,
size_t i) {
static_assert(INT_SIZE == 256,
"Only 256 is supported as an int size right now.");
// This version uses dyadic floats with 256 bit mantissas to perform the same
// calculation as above. Due to floating point imprecision it is only accurate
// for the first 50 digits, but it's much faster. Since even 128 bit long
// doubles are only accurate to ~35 digits, the 50 digits of accuracy are
// enough for these floats to be converted back and forth safely. This is
// ideal for avoiding the size of the long double table.
const int shift_amount = exponent + CALC_SHIFT_CONST - (9 * i);
if (shift_amount < 0) {
return 1;
}
fputil::DyadicFloat<INT_SIZE> num(false, 0, 1);
constexpr cpp::UInt<INT_SIZE> MOD_SIZE =
(cpp::UInt<INT_SIZE>(1000000000)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));
constexpr cpp::UInt<INT_SIZE> FIVE_EXP_MINUS_NINE_MANT{
{0xf387295d242602a7, 0xfdd7645e011abac9, 0x31680a88f8953030,
0x89705f4136b4a597}};
static const fputil::DyadicFloat<INT_SIZE> FIVE_EXP_MINUS_NINE(
false, -276, FIVE_EXP_MINUS_NINE_MANT);
if (i > 0) {
fputil::DyadicFloat<INT_SIZE> fives = fputil::pow_n(FIVE_EXP_MINUS_NINE, i);
num = fives;
}
num = mul_pow_2(num, shift_amount);
// Adding one is part of the formula.
cpp::UInt<INT_SIZE> int_num = static_cast<cpp::UInt<INT_SIZE>>(num) + 1;
if (int_num > MOD_SIZE) {
auto rem =
int_num
.div_uint32_times_pow_2(
1000000000, CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0))
.value();
int_num = rem;
}
cpp::UInt<MID_INT_SIZE> result = int_num;
return result;
}
// The formula for the table when i is negative (or zero) is as follows:
// floor(10^(-9i) * 2^(c_0 - e)) % (10^9 * 2^c_0)
// Since we know i is always negative, we just take it as unsigned and treat it
@ -136,40 +267,44 @@ get_table_positive(int exponent, size_t i, const size_t constant) {
// calculations.
// The formula being used looks more like this:
// floor(10^(9*(-i)) * 2^(c_0 + (-e))) % (10^9 * 2^c_0)
LIBC_INLINE cpp::UInt<MID_INT_SIZE> get_table_negative(int exponent, size_t i,
const size_t constant) {
constexpr size_t INT_SIZE = 1024;
int shift_amount = constant - exponent;
template <size_t INT_SIZE>
LIBC_INLINE cpp::UInt<MID_INT_SIZE> get_table_negative(int exponent, size_t i) {
int shift_amount = CALC_SHIFT_CONST - exponent;
cpp::UInt<INT_SIZE> num(1);
// const cpp::UInt<INT_SIZE> MOD_SIZE =
// (cpp::UInt<INT_SIZE>(1) << constant) * 1000000000;
constexpr cpp::UInt<INT_SIZE> MOD_SIZE =
(cpp::UInt<INT_SIZE>(1000000000)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));
constexpr uint64_t TEN_EXP_NINE = 1000000000;
constexpr uint64_t FIVE_EXP_NINE = 1953125;
size_t ten_blocks = i;
size_t five_blocks = 0;
if (shift_amount < 0) {
int block_shifts = (-shift_amount) / 9;
int block_shifts = (-shift_amount) / BLOCK_SIZE;
if (block_shifts < static_cast<int>(ten_blocks)) {
ten_blocks = ten_blocks - block_shifts;
five_blocks = block_shifts;
shift_amount = shift_amount + (block_shifts * 9);
shift_amount = shift_amount + (block_shifts * BLOCK_SIZE);
} else {
ten_blocks = 0;
five_blocks = i;
shift_amount = shift_amount + (i * 9);
shift_amount = shift_amount + (i * BLOCK_SIZE);
}
}
if (five_blocks > 0) {
cpp::UInt<INT_SIZE> fives(FIVE_EXP_NINE);
fives.pow_n(five_blocks);
num *= fives;
num = fives;
}
if (ten_blocks > 0) {
cpp::UInt<INT_SIZE> tens(TEN_EXP_NINE);
tens.pow_n(ten_blocks);
num *= tens;
if (five_blocks <= 0) {
num = tens;
} else {
num *= tens;
}
}
if (shift_amount > 0) {
@ -177,12 +312,61 @@ LIBC_INLINE cpp::UInt<MID_INT_SIZE> get_table_negative(int exponent, size_t i,
} else {
num = num >> (-shift_amount);
}
// if (num > MOD_SIZE) {
// num = num % MOD_SIZE;
// }
if (num > MOD_SIZE) {
auto rem =
num.div_uint32_times_pow_2(
1000000000, CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0))
.value();
num = rem;
}
return num;
}
template <size_t INT_SIZE>
LIBC_INLINE cpp::UInt<MID_INT_SIZE> get_table_negative_df(int exponent,
size_t i) {
static_assert(INT_SIZE == 256,
"Only 256 is supported as an int size right now.");
// This version uses dyadic floats with 256 bit mantissas to perform the same
// calculation as above. Due to floating point imprecision it is only accurate
// for the first 50 digits, but it's much faster. Since even 128 bit long
// doubles are only accurate to ~35 digits, the 50 digits of accuracy are
// enough for these floats to be converted back and forth safely. This is
// ideal for avoiding the size of the long double table.
int shift_amount = CALC_SHIFT_CONST - exponent;
fputil::DyadicFloat<INT_SIZE> num(false, 0, 1);
constexpr cpp::UInt<INT_SIZE> MOD_SIZE =
(cpp::UInt<INT_SIZE>(1000000000)
<< (CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0)));
constexpr cpp::UInt<INT_SIZE> TEN_EXP_NINE_MANT(1000000000);
static const fputil::DyadicFloat<INT_SIZE> TEN_EXP_NINE(false, 0,
TEN_EXP_NINE_MANT);
if (i > 0) {
fputil::DyadicFloat<INT_SIZE> tens = fputil::pow_n(TEN_EXP_NINE, i);
num = tens;
}
num = mul_pow_2(num, shift_amount);
cpp::UInt<INT_SIZE> int_num = static_cast<cpp::UInt<INT_SIZE>>(num);
if (int_num > MOD_SIZE) {
auto rem =
int_num
.div_uint32_times_pow_2(
1000000000, CALC_SHIFT_CONST + (IDX_SIZE > 1 ? IDX_SIZE : 0))
.value();
int_num = rem;
}
cpp::UInt<MID_INT_SIZE> result = int_num;
return result;
}
LIBC_INLINE uint32_t fast_uint_mod_1e9(const cpp::UInt<MID_INT_SIZE> &val) {
// The formula for mult_const is:
// 1 + floor((2^(bits in target integer size + log_2(divider))) / divider)
@ -205,7 +389,9 @@ LIBC_INLINE uint32_t mul_shift_mod_1e9(const MantissaInt mantissa,
wide_mant[0] = mantissa & (uint64_t(-1));
wide_mant[1] = mantissa >> 64;
val = (val * wide_mant) >> shift_amount;
return fast_uint_mod_1e9(val);
return val.div_uint32_times_pow_2(1000000000, 0).value()[0];
// return fast_uint_mod_1e9(val);
}
} // namespace internal
@ -236,8 +422,6 @@ class FloatToString {
static constexpr int MANT_WIDTH = fputil::MantissaWidth<T>::VALUE;
static constexpr int EXP_BIAS = fputil::FPBits<T>::EXPONENT_BIAS;
// constexpr void init_convert();
public:
LIBC_INLINE constexpr FloatToString(T init_float) : float_bits(init_float) {
is_negative = float_bits.get_sign();
@ -273,18 +457,49 @@ public:
// 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 + 15) / 16;
exponent < 0
? 0
: static_cast<uint32_t>(exponent + (IDX_SIZE - 1)) / IDX_SIZE;
uint32_t temp_shift_amount =
POW10_ADDITIONAL_BITS_TABLE + (16 * idx) - exponent;
const uint32_t shift_amount = temp_shift_amount;
// shift_amount = -(c0 - exponent) = c_0 + 16 * ceil(exponent/16) -
// exponent
int32_t i = block_index;
cpp::UInt<MID_INT_SIZE> val;
val = POW10_SPLIT[POW10_OFFSET[idx] + i];
#if defined(LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT)
// ----------------------- DYADIC FLOAT CALC MODE ------------------------
const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
val = internal::get_table_positive_df<256>(IDX_SIZE * idx, block_index);
#elif defined(LIBC_COPT_FLOAT_TO_STR_USE_INT_CALC)
// ---------------------------- INT CALC MODE ----------------------------
const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
const uint64_t MAX_POW_2_SIZE =
exponent + 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>(IDX_SIZE * idx, block_index);
} else if (MAX_INT_SIZE < 2048) {
val = internal::get_table_positive<2048>(IDX_SIZE * idx, block_index);
} else if (MAX_INT_SIZE < 4096) {
val = internal::get_table_positive<4096>(IDX_SIZE * idx, block_index);
} else if (MAX_INT_SIZE < 8192) {
val = internal::get_table_positive<8192>(IDX_SIZE * idx, block_index);
} else {
val = internal::get_table_positive<16384>(IDX_SIZE * idx, block_index);
}
#else
// ----------------------------- TABLE MODE ------------------------------
const int32_t SHIFT_CONST = TABLE_SHIFT_CONST;
val = POW10_SPLIT[POW10_OFFSET[idx] + block_index];
#endif
const uint32_t shift_amount = SHIFT_CONST + (IDX_SIZE * idx) - exponent;
const uint32_t digits =
internal::mul_shift_mod_1e9(mantissa, val, (int32_t)(shift_amount));
return digits;
@ -295,25 +510,58 @@ public:
LIBC_INLINE constexpr BlockInt get_negative_block(int block_index) {
if (exponent < 0) {
const int32_t idx = -exponent / 16;
uint32_t i = block_index;
const int32_t idx = -exponent / IDX_SIZE;
cpp::UInt<MID_INT_SIZE> val;
#if defined(LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT)
// ----------------------- DYADIC FLOAT CALC MODE ------------------------
const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
val =
internal::get_table_negative_df<256>(idx * IDX_SIZE, block_index + 1);
#elif defined(LIBC_COPT_FLOAT_TO_STR_USE_INT_CALC)
// ---------------------------- INT CALC MODE ----------------------------
const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
const uint64_t TEN_BLOCKS = (block_index + 1) * BLOCK_SIZE;
const uint64_t MAX_INT_SIZE = internal::log2_pow5(TEN_BLOCKS);
if (MAX_INT_SIZE < 1024) {
val =
internal::get_table_negative<1024>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 2048) {
val =
internal::get_table_negative<2048>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 4096) {
val =
internal::get_table_negative<4096>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 8192) {
val =
internal::get_table_negative<8192>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 16384) {
val = internal::get_table_negative<16384>(idx * IDX_SIZE,
block_index + 1);
} else {
val = internal::get_table_negative<32768>(idx * IDX_SIZE,
block_index + 1);
}
#else
// ----------------------------- TABLE MODE ------------------------------
// if the requested block is zero
if (i < MIN_BLOCK_2[idx]) {
const int32_t SHIFT_CONST = TABLE_SHIFT_CONST;
if (block_index < MIN_BLOCK_2[idx]) {
return 0;
}
const int32_t shift_amount =
POW10_ADDITIONAL_BITS_TABLE + (-exponent - 16 * idx);
const uint32_t p = POW10_OFFSET_2[idx] + i - MIN_BLOCK_2[idx];
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]) {
return 0;
}
cpp::UInt<MID_INT_SIZE> table_val = POW10_SPLIT_2[p];
// cpp::UInt<MID_INT_SIZE> calculated_val =
// get_table_negative(idx * 16, i + 1, POW10_ADDITIONAL_BITS_CALC);
val = POW10_SPLIT_2[p];
#endif
const int32_t shift_amount = SHIFT_CONST + (-exponent - IDX_SIZE * idx);
uint32_t digits =
internal::mul_shift_mod_1e9(mantissa, table_val, shift_amount);
internal::mul_shift_mod_1e9(mantissa, val, shift_amount);
return digits;
} else {
return 0;
@ -331,8 +579,10 @@ public:
LIBC_INLINE constexpr size_t get_positive_blocks() {
if (exponent >= -MANT_WIDTH) {
const uint32_t idx =
exponent < 0 ? 0 : static_cast<uint32_t>(exponent + 15) / 16;
const uint32_t len = internal::length_for_num(idx, MANT_WIDTH);
exponent < 0
? 0
: static_cast<uint32_t>(exponent + (IDX_SIZE - 1)) / IDX_SIZE;
const uint32_t len = internal::length_for_num(idx * IDX_SIZE, MANT_WIDTH);
return len;
} else {
return 0;
@ -342,30 +592,53 @@ public:
// 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) {
const int32_t idx = -exponent / 16;
#ifdef LIBC_COPT_FLOAT_TO_STR_NO_TABLE
return false;
#else
const int32_t idx = -exponent / IDX_SIZE;
const uint32_t p = POW10_OFFSET_2[idx] + 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() {
return MIN_BLOCK_2[-exponent / 16];
#ifdef LIBC_COPT_FLOAT_TO_STR_NO_TABLE
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
}
};
// template <> constexpr void FloatToString<float>::init_convert() { ; }
#ifndef LONG_DOUBLE_IS_DOUBLE
// --------------------------- LONG DOUBLE FUNCTIONS ---------------------------
// template <> constexpr void FloatToString<double>::init_convert() { ; }
// template <> constexpr void FloatToString<long double>::init_convert() {
// // TODO: More here.
// ;
// }
template <>
LIBC_INLINE constexpr size_t FloatToString<long double>::get_positive_blocks() {
if (exponent >= -MANT_WIDTH) {
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, MANT_WIDTH);
return len;
} else {
return 0;
}
}
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
}
template <>
@ -377,29 +650,52 @@ template <>
LIBC_INLINE constexpr BlockInt
FloatToString<long double>::get_positive_block(int block_index) {
if (exponent >= -MANT_WIDTH) {
const uint32_t pos_exp = (exponent < 0 ? 0 : exponent);
uint32_t temp_shift_amount =
POW10_ADDITIONAL_BITS_CALC + pos_exp - exponent;
const uint32_t shift_amount = temp_shift_amount;
// shift_amount = -(c0 - exponent) = c_0 + 16 * ceil(exponent/16) -
// exponent
// 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
int32_t i = block_index;
cpp::UInt<MID_INT_SIZE> val;
if (exponent + POW10_ADDITIONAL_BITS_CALC < 1024) {
val = internal::get_table_positive<1024>(pos_exp, i,
POW10_ADDITIONAL_BITS_CALC);
} else if (exponent + POW10_ADDITIONAL_BITS_CALC < 4096) {
val = internal::get_table_positive<4096>(pos_exp, i,
POW10_ADDITIONAL_BITS_CALC);
} else if (exponent + POW10_ADDITIONAL_BITS_CALC < 8192) {
val = internal::get_table_positive<8192>(pos_exp, i,
POW10_ADDITIONAL_BITS_CALC);
#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 =
exponent + 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 {
val = internal::get_table_positive<16384>(pos_exp, i,
POW10_ADDITIONAL_BITS_CALC);
val = internal::get_table_positive<16384>(pos_exp, block_index);
}
#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));
@ -413,31 +709,62 @@ template <>
LIBC_INLINE constexpr BlockInt
FloatToString<long double>::get_negative_block(int block_index) {
if (exponent < 0) {
const int32_t idx = -exponent / 16;
uint32_t i = -1 - block_index;
const int32_t idx = -exponent / IDX_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;
// if the requested block is zero
if (i <= MIN_BLOCK_2[idx]) {
if (block_index <= MIN_BLOCK_2[idx]) {
return 0;
}
const int32_t shift_amount =
POW10_ADDITIONAL_BITS_CALC + (-exponent - 16 * idx);
const uint32_t p = POW10_OFFSET_2[idx] + i - MIN_BLOCK_2[idx];
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]) {
return 0;
}
val = POW10_SPLIT_2[p];
#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;
// cpp::UInt<MID_INT_SIZE> table_val = POW10_SPLIT_2[p];
cpp::UInt<MID_INT_SIZE> calculated_val = internal::get_table_negative(
idx * 16, i + 1, POW10_ADDITIONAL_BITS_CALC);
BlockInt digits =
internal::mul_shift_mod_1e9(mantissa, calculated_val, shift_amount);
val = internal::get_table_negative_df<256>(idx * IDX_SIZE, block_index + 1);
#else // table mode
// ----------------------------- INT CALC MODE -----------------------------
const int32_t SHIFT_CONST = CALC_SHIFT_CONST;
const uint64_t TEN_BLOCKS = (block_index + 1) * BLOCK_SIZE;
const uint64_t MAX_INT_SIZE = internal::log2_pow5(TEN_BLOCKS);
if (MAX_INT_SIZE < 1024) {
val = internal::get_table_negative<1024>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 2048) {
val = internal::get_table_negative<2048>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 4096) {
val = internal::get_table_negative<4096>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 8192) {
val = internal::get_table_negative<8192>(idx * IDX_SIZE, block_index + 1);
} else if (MAX_INT_SIZE < 16384) {
val =
internal::get_table_negative<16384>(idx * IDX_SIZE, block_index + 1);
} else {
val = internal::get_table_negative<16384 + 8192>(idx * IDX_SIZE,
block_index + 1);
}
#endif
const int32_t shift_amount = SHIFT_CONST + (-exponent - IDX_SIZE * idx);
BlockInt digits = internal::mul_shift_mod_1e9(mantissa, val, shift_amount);
return digits;
} else {
return 0;
}
}
#endif // LONG_DOUBLE_IS_DOUBLE
} // namespace __llvm_libc
#endif // LLVM_LIBC_SRC_SUPPORT_FLOAT_TO_STRING_H

View File

@ -12,6 +12,11 @@
#include <stddef.h>
#include <stdint.h>
constexpr size_t TABLE_SHIFT_CONST = 120;
constexpr size_t IDX_SIZE = 16;
constexpr size_t MID_INT_SIZE = 192;
static const uint16_t POW10_OFFSET[64] = {
0, 2, 5, 8, 12, 16, 21, 26, 32, 39, 46, 54, 62,
71, 80, 90, 100, 111, 122, 134, 146, 159, 173, 187, 202, 217,

View File

@ -0,0 +1,59 @@
//===-- Ryu Constants for long double conversion ----------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIBC_SRC_SUPPORT_RYU_LONG_DOUBLE_CONSTANTS_H
#define LLVM_LIBC_SRC_SUPPORT_RYU_LONG_DOUBLE_CONSTANTS_H
#include <stddef.h>
#include <stdint.h>
constexpr size_t TABLE_SHIFT_CONST = 120;
constexpr size_t IDX_SIZE = 128;
constexpr size_t MID_INT_SIZE = (256 + 64);
static const uint32_t POW10_OFFSET[] = {
0, 4, 13, 26, 43, 64, 90, 120, 154, 193, 236,
283, 334, 390, 450, 514, 582, 655, 732, 813, 899, 989,
1083, 1181, 1284, 1391, 1502, 1618, 1738, 1862, 1990, 2123, 2260,
2401, 2547, 2697, 2851, 3009, 3172, 3339, 3510, 3686, 3866, 4050,
4238, 4431, 4628, 4829, 5034, 5244, 5458, 5676, 5899, 6126, 6357,
6592, 6832, 7076, 7324, 7577, 7834, 8095, 8360, 8630, 8904, 9182,
9465, 9752, 10043, 10338, 10638, 10942, 11250, 11563, 11880, 12201, 12526,
12856, 13190, 13528, 13871, 14218, 14569, 14924, 15284, 15648, 16016, 16388,
16765, 17146, 17531, 17921, 18315, 18713, 19115, 19522, 19933, 20348, 20768,
21192, 21620, 22052, 22489, 22930, 23375, 23825, 24279, 24737, 25199, 25666,
26137, 26612, 27092, 27576, 28064, 28556, 29053, 29554, 30059, 30568, 31082,
31600, 32122, 32649, 33180, 33715, 34254, 34798, 35346};
static const uint32_t POW10_OFFSET_2[130] = {
0, 15, 44, 83, 131, 190, 259, 338, 426, 524, 633,
751, 879, 1017, 1166, 1324, 1492, 1670, 1858, 2056, 2264, 2482,
2709, 2947, 3195, 3453, 3720, 3997, 4285, 4582, 4889, 5206, 5534,
5871, 6218, 6575, 6941, 7318, 7705, 8102, 8508, 8925, 9352, 9788,
10234, 10690, 11157, 11633, 12119, 12615, 13122, 13638, 14164, 14700, 15245,
15801, 16367, 16943, 17528, 18124, 18730, 19345, 19970, 20605, 21251, 21906,
22571, 23246, 23931, 24626, 25331, 26046, 26770, 27505, 28250, 29004, 29768,
30543, 31328, 32122, 32926, 33740, 34565, 35399, 36243, 37097, 37961, 38835,
39719, 40613, 41516, 42430, 43354, 44287, 45230, 46184, 47148, 48121, 49104,
50097, 51100, 52113, 53136, 54169, 55212, 56265, 57328, 58400, 59482, 60575,
61678, 62790, 63912, 65045, 66188, 67340, 68502, 69674, 70856, 72048, 73250,
74462, 75684, 76916, 78158, 79409, 80670, 81942, 83224, 84515};
static const uint16_t MIN_BLOCK_2[130] = {
0, 0, 4, 9, 13, 17, 21, 26, 30, 34, 39, 43, 47, 51, 56,
60, 64, 68, 73, 77, 81, 86, 90, 94, 98, 103, 107, 111, 116, 120,
124, 128, 133, 137, 141, 146, 150, 154, 158, 163, 167, 171, 176, 180, 184,
188, 193, 197, 201, 205, 210, 214, 218, 223, 227, 231, 235, 240, 244, 248,
253, 257, 261, 265, 270, 274, 278, 283, 287, 291, 295, 300, 304, 308, 313,
317, 321, 325, 330, 334, 338, 342, 347, 351, 355, 360, 364, 368, 372, 377,
381, 385, 390, 394, 398, 402, 407, 411, 415, 420, 424, 428, 432, 437, 441,
445, 450, 454, 458, 462, 467, 471, 475, 479, 484, 488, 492, 497, 501, 505,
509, 514, 518, 522, 527, 531, 535, 539, 544, 0};
#endif // LLVM_LIBC_SRC_SUPPORT_RYU_LONG_DOUBLE_CONSTANTS_H

View File

@ -99,6 +99,8 @@ add_object_library(
libc.src.__support.uint128
libc.src.__support.integer_to_string
libc.src.__support.float_to_string
# COMPILE_OPTIONS
# -DLIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
)

View File

@ -28,8 +28,8 @@ int convert(Writer *writer, const FormatSection &to_conv) {
if (!to_conv.has_conv)
return writer->write(to_conv.raw_string);
#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
// TODO(michaelrj): Undo this once decimal long double support is done.
#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) && \
defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
if (to_conv.length_modifier == LengthModifier::L) {
switch (to_conv.conv_name) {
case 'f':

View File

@ -29,6 +29,14 @@
#define LIBC_COPT_PRINTF_INDEX_ARR_LEN 128
#endif
// TODO(michaelrj): Provide a proper interface for these options.
// LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT
// LIBC_COPT_FLOAT_TO_STR_USE_DYADIC_FLOAT_LD
// LIBC_COPT_FLOAT_TO_STR_USE_INT_CALC
// LIBC_COPT_FLOAT_TO_STR_NO_TABLE
// LIBC_COPT_PRINTF_HEX_LONG_DOUBLE
// TODO(michaelrj): Move the other printf configuration options into this file.
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_PRINTF_CONFIG_H

View File

@ -930,16 +930,19 @@ TEST_F(LlvmLibcSPrintfTest, FloatDecimalConv) {
// TODO: Fix long doubles (needs bigger table or alternate algorithm.)
// Currently the table values are generated, which is very slow.
/*
written = __llvm_libc::sprintf(buff, "%Lf", 1.0L);
ASSERT_STREQ_LEN(written, buff, "1.000000");
written = __llvm_libc::sprintf(buff, "%Lf", 1e100L);
ASSERT_STREQ_LEN(written, buff,
"99999999999999999996693535322073426194986990198284960792713"
"91541752018669482644324418977840117055488.000000");
written = __llvm_libc::sprintf(buff, "%Lf", 1.0L);
ASSERT_STREQ_LEN(written, buff, "1.000000");
char big_buff[10000];
// written = __llvm_libc::sprintf(big_buff, "%Lf", 0x1p16383L);
written = __llvm_libc::sprintf(big_buff, "%Lf", 1e1000L);
ASSERT_STREQ_LEN(
written, big_buff,
@ -1031,7 +1034,122 @@ TEST_F(LlvmLibcSPrintfTest, FloatDecimalConv) {
"739074789794941408428328217107759915202650066155868439585510978709442590"
"231934194956788626761834746430104077432547436359522462253411168467463134"
"24896.000000");
*/
written = __llvm_libc::sprintf(big_buff, "%.10Lf", 1e-10L);
ASSERT_STREQ_LEN(
written, big_buff, "0.0000000001");
written = __llvm_libc::sprintf(big_buff, "%.7500Lf", 1e-4900L);
ASSERT_STREQ_LEN(
written, big_buff,
"0."
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000000000000000000000000000000000000000000000000000000000000000000000000"
"000099999999999999999996962764452956071352139203248614920751610856665084"
"549214352477698417183862158583009348897567779527408501588132175167211539"
"462139941448204886585901454195352527238724272760638086779284030512649793"
"039219351187928723378036480041948464946018272171365770411701020666925613"
"422460317465324758217878522666789603627480490870456508256359180089236338"
"765625231186929290294207420828927406735690318849109129700396907705735097"
"663944722727287361650042373203763784830198232253311807069225650324196304"
"532045014970637489181357566354288111205943347410488298480279857453705249"
"232862728556860184412369114663536200895729846877559808001004454634804626"
"541455540260282018142615835686583304903486353937549394736905011798466731"
"536563240053860118551127061960208467764243724656897127545613968909523389"
"577188368809623987105800147797280462974804046545425080530020901531407223"
"191237123282274818236437397994019915368657474589800678444589412286037789"
"891525464936023205313685584525510094270344601331453730179416773626565262"
"480345858564672442896904520146956686863172737711483866766404977719744767"
"834324844875237277613991088218774564658513875732403456058414595576806383"
"115554713240005982141397577420073082470139244845624915873825746771661332"
"098677966580506186966978746832443976821987300902957597498388211921362869"
"017846215557612829071692275292036211064515305528052919611691470945774714"
"135516559501572279732350629089770249554808690411603894492333360300589658"
"470898965370892774715815089075170720164713889237058574941489766701880158"
"060081295483989540170337129032188818293132770882381428397119039835946745"
"549356649433406617266370644136291924838857814675939156677910783740103207"
"523299367093130816446415259371931925208362367989095199399211644084543790"
"110432339056231037520216864358899218874658268610955002763260912337688947"
"822453100821038299301092582962825965939081817836419126254832772002214908"
"085575905761843610944187009818156363893015929300295112598059949496854566"
"638748010633726861510500653821408135845840123073754133549077708843800674"
"328440913743105608636458354618912183716456158809545183074062249922212944"
"249667793845728355381309084891765979111348980470647082269921872595470473"
"719354467594516320911964549508538492057120740224559944452120552719041944"
"961475548547884309626382512432626380881023756568143060204097921571153170"
"723817845809196253498326358439807445210362177680590181657555380795450462"
"223805222580359379367452693270553602179122419370586308101820559214330382"
"570449525088342437216896462077260223998756027453411520977536701491759878"
"422771447006016890777855573925295187921971811871399320142563330377888532"
"179817332113");
/*
written = __llvm_libc::sprintf(buff, "%La", 0.1L);
#if defined(SPECIAL_X86_LONG_DOUBLE)

View File

@ -0,0 +1,204 @@
"""
//===-- Table Generator for Ryu Printf ------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
This file is used to generate the tables of values in
src/__support/ryu_constants.h and ryu_long_double constants.h. To use it, set
the constants at the top of the file to the values you want to use for the Ryu
algorithm, then run this file. It will output the appropriate tables to stdout,
so it's recommended to pipe stdout to a file. The following is a brief
explenation of each of the constants.
BLOCK_SIZE:
Default: 9
The number of digits that will be calculated together in a block.
Don't touch this unless you really know what you're doing.
CONSTANT:
Default: 120
Also known as c_0 and c_1 in the Ryu Printf paper and SHIFT_CONST in
float_to_string.h.
The table value is shifted left by this amount, and the final value is
shifted right by this amount. It effectively makes the table value a fixed
point number with the decimal point at the bit that is CONSTANT bits from
the right.
Higher values increase accuracy, but also require higher MID_INT_WIDTH
values to fit the result.
IDX_SIZE:
Default: 16
By increasing the MOD_SIZE slightly we can significantly decrease the number
of table entries we need.
We can divide the number of table entries by IDX_SIZE, and multiply MOD_SIZE
by 2^IDX_SIZE, and the math still works out.
This optimization isn't mentioned in the original Ryu Printf paper but it
saves a lot of space.
MID_INT_WIDTH:
Default: 192
This is the size of integer that the tables use. It's called mid int because
it's the integer used in the middle of the calculation. There are large ints
used to calculate the mid int table values, then those are used to calculate
the small int final values.
This must be divisible by 64 since each table entry is an array of 64 bit
integers.
If this is too small, then the results will get cut off. It should be at
least CONSTANT + IDX_SIZE + log_2(10^9) to be able to fit the table values.
MANT_WIDTH:
The width of the widest float mantissa that this table will work for.
This has a small effect on table size.
EXP_WIDTH:
The width of the widest float exponent that this table will work for.
This has a large effect on table size.
Specifically, table size is proportional to the square of this number.
"""
BLOCK_SIZE = 9
# Values for double
# CONSTANT = 120
# IDX_SIZE = 16
# MANT_WIDTH = 52
# EXP_WIDTH = 11
# MID_INT_SIZE = 192
# Values for 128 bit float
CONSTANT = 120
IDX_SIZE = 128
MANT_WIDTH = 112
EXP_WIDTH = 15
MID_INT_SIZE = 256 + 64
MAX_EXP = 2 ** (EXP_WIDTH - 1)
POSITIVE_ARR_SIZE = MAX_EXP // IDX_SIZE
NEGATIVE_ARR_SIZE = (MAX_EXP // IDX_SIZE) + ((MANT_WIDTH + (IDX_SIZE - 1)) // IDX_SIZE)
MOD_SIZE = (10**BLOCK_SIZE) << (CONSTANT + (IDX_SIZE if IDX_SIZE > 1 else 0))
# floor(5^(-9i) * 2^(e + c_1 - 9i) + 1) % (10^9 * 2^c_1)
def get_table_positive(exponent, i):
pow_of_two = 1 << (exponent + CONSTANT - (BLOCK_SIZE * i))
pow_of_five = 5 ** (BLOCK_SIZE * i)
result = (pow_of_two // pow_of_five) + 1
return result % MOD_SIZE
# floor(10^(9*(-i)) * 2^(c_0 + (-e))) % (10^9 * 2^c_0)
def get_table_negative(exponent, i):
result = 1
pow_of_ten = 10 ** (BLOCK_SIZE * i)
shift_amount = CONSTANT - exponent
if shift_amount < 0:
result = pow_of_ten >> (-shift_amount)
else:
result = pow_of_ten << (shift_amount)
return result % MOD_SIZE
# Returns floor(log_10(2^e)); requires 0 <= e <= 42039.
def ceil_log10_pow2(e):
return ((e * 0x13441350FBD) >> 42) + 1
def length_for_num(idx, index_size=IDX_SIZE):
return (
ceil_log10_pow2(idx * index_size) + ceil_log10_pow2(MANT_WIDTH) + BLOCK_SIZE - 1
) // BLOCK_SIZE
def get_64bit_window(num, index):
return (num >> (index * 64)) % (2**64)
def mid_int_to_str(num):
outstr = " {"
outstr += str(get_64bit_window(num, 0)) + "u"
for i in range(1, MID_INT_SIZE // 64):
outstr += ", " + str(get_64bit_window(num, i)) + "u"
outstr += "},"
return outstr
def print_positive_table_for_idx(idx):
positive_blocks = length_for_num(idx)
for i in range(positive_blocks):
table_val = get_table_positive(idx * IDX_SIZE, i)
# print(hex(table_val))
print(mid_int_to_str(table_val))
return positive_blocks
def print_negative_table_for_idx(idx):
i = 0
min_block = -1
table_val = 0
MIN_USEFUL_VAL = 2 ** (CONSTANT - (MANT_WIDTH + 2))
# Iterate through the zero blocks
while table_val < MIN_USEFUL_VAL:
i += 1
table_val = get_table_negative((idx) * IDX_SIZE, i)
else:
i -= 1
min_block = i
# Iterate until another zero block is found
while table_val >= MIN_USEFUL_VAL:
table_val = get_table_negative((idx) * IDX_SIZE, i + 1)
if table_val >= MIN_USEFUL_VAL:
print(mid_int_to_str(table_val))
i += 1
return i - min_block, min_block
positive_size_arr = [0] * (POSITIVE_ARR_SIZE + 1)
negative_size_arr = [0] * (NEGATIVE_ARR_SIZE + 1)
min_block_arr = [0] * (NEGATIVE_ARR_SIZE + 1)
acc = 0
if MOD_SIZE > (2**MID_INT_SIZE):
print(
"Mod size is too big for current MID_INT_SIZE by a factor of",
MOD_SIZE // (2**MID_INT_SIZE),
)
else:
print("static const uint64_t POW10_SPLIT[][" + str(MID_INT_SIZE // 64) + "] = {")
for idx in range(0, POSITIVE_ARR_SIZE):
num_size = print_positive_table_for_idx(idx)
acc += num_size
positive_size_arr[idx + 1] = acc
print("};")
print(
"static const uint32_t POW10_OFFSET_2[" + str(len(positive_size_arr)) + "] = {",
str(positive_size_arr)[1:-2],
"};",
)
print("static const uint64_t POW10_SPLIT_2[][" + str(MID_INT_SIZE // 64) + "] = {")
for idx in range(0, NEGATIVE_ARR_SIZE):
num_size, min_block = print_negative_table_for_idx(idx)
acc += num_size
negative_size_arr[idx + 1] = acc
min_block_arr[idx] = min_block
print("};")
print(
"static const uint32_t POW10_OFFSET_2[" + str(len(negative_size_arr)) + "] = {",
str(negative_size_arr)[1:-2],
"};",
)
print(
"static const uint16_t MIN_BLOCK_2[" + str(len(min_block_arr)) + "] = {",
str(min_block_arr)[1:-2],
"};",
)

View File

@ -310,10 +310,14 @@ libc_support_library(
hdrs = [
"src/__support/float_to_string.h",
"src/__support/ryu_constants.h",
"src/__support/ryu_long_double_constants.h",
],
# This is temporarily commented out since the table is too large to land in the same patch as the rest of the changes.
# defines = ["LIBC_COPT_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE"],
deps = [
":__support_common",
":__support_cpp_type_traits",
":__support_fputil_dyadic_float",
":__support_fputil_fp_bits",
":__support_libc_assert",
":__support_uint",
@ -759,6 +763,7 @@ libc_support_library(
":__support_common",
":__support_fputil_multiply_add",
":__support_number_pair",
":libc_root",
],
)
@ -772,6 +777,7 @@ libc_support_library(
":__support_fputil_multiply_add",
":__support_macros_optimization",
":__support_uint",
":libc_root",
],
)