392 changes: 375 additions & 17 deletions libc/src/stdio/printf_core/float_dec_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,8 @@ class FloatWriter {
// Here we have to recalculate the total number of digits since the
// exponent's width may have changed. We're only adding 1 to exponent
// width since exp_str appends the sign.
total_digits = 1 + number_digits + 1 + exponent_width;
total_digits =
(has_decimal_point ? 1 : 0) + number_digits + 1 + exponent_width;

// Normally write_left_padding is called by flush_buffer but since we're
// rounding up all of the digits, the ones in the buffer are wrong and
Expand Down Expand Up @@ -580,38 +581,38 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
} else {

const uint32_t maximum = precision - BLOCK_SIZE * i;
uint32_t lastDigit = 0;
uint32_t last_digit = 0;
for (uint32_t k = 0; k < BLOCK_SIZE - maximum; ++k) {
lastDigit = digits % 10;
last_digit = digits % 10;
digits /= 10;
}
RoundDirection round;
// Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
const int32_t requiredTwos =
-exponent - MANT_WIDTH - (int32_t)precision - 1;
-exponent - MANT_WIDTH - static_cast<int32_t>(precision) - 1;
const bool trailingZeros =
requiredTwos <= 0 ||
(requiredTwos < 60 &&
multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
(uint32_t)requiredTwos));
static_cast<uint32_t>(requiredTwos)));
switch (fputil::get_round()) {
case FE_TONEAREST:
// Round to nearest, if it's exactly halfway then round to even.
if (lastDigit != 5) {
round = lastDigit > 5 ? RoundDirection::Up : RoundDirection::Down;
if (last_digit != 5) {
round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
} else {
round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
}
break;
case FE_DOWNWARD:
if (is_negative && (!trailingZeros || lastDigit > 0)) {
if (is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
}
break;
case FE_UPWARD:
if (!is_negative && (!trailingZeros || lastDigit > 0)) {
if (!is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
Expand Down Expand Up @@ -746,37 +747,46 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,

// This is the last block.
const uint32_t maximum = precision + 1 - digits_written;
uint32_t lastDigit = 0;
uint32_t last_digit = 0;
for (uint32_t k = 0; k < last_block_size - maximum; ++k) {
lastDigit = digits % 10;
last_digit = digits % 10;
digits /= 10;
}

// If the last block we read doesn't have the digit after the end of what
// we'll print, then we need to read the next block to get that digit.
if (maximum == last_block_size) {
BlockInt extra_block = float_converter.get_block(cur_block - 1);
last_digit = extra_block / ((MAX_BLOCK / 10) + 1);
}

RoundDirection round;
// Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
const int32_t requiredTwos = -exponent - MANT_WIDTH - (int32_t)precision - 1;
const int32_t requiredTwos =
-exponent - MANT_WIDTH - static_cast<int32_t>(precision) - 1;
const bool trailingZeros =
requiredTwos <= 0 ||
(requiredTwos < 60 &&
multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
(uint32_t)requiredTwos));
static_cast<uint32_t>(requiredTwos)));
switch (fputil::get_round()) {
case FE_TONEAREST:
// Round to nearest, if it's exactly halfway then round to even.
if (lastDigit != 5) {
round = lastDigit > 5 ? RoundDirection::Up : RoundDirection::Down;
if (last_digit != 5) {
round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
} else {
round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
}
break;
case FE_DOWNWARD:
if (is_negative && (!trailingZeros || lastDigit > 0)) {
if (is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
}
break;
case FE_UPWARD:
if (!is_negative && (!trailingZeros || lastDigit > 0)) {
if (!is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
Expand All @@ -794,6 +804,334 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
return WRITE_OK;
}

template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -MANT_WIDTH
constexpr int32_t MANT_WIDTH = fputil::MantissaWidth<T>::VALUE;
bool is_negative = float_bits.get_sign();
int exponent = float_bits.get_exponent();
MantissaInt 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.
const size_t init_precision = to_conv.precision <= 0
? (to_conv.precision == 0 ? 1 : 6)
: to_conv.precision;

// Then, if a conversion with style E would have an exponent of X
// (base_10_exp):
int base_10_exp = 0;
// If P > X >= -4 the conversion is with style F and precision P - (X + 1).
// Otherwise, the conversion is with style E and precision P - 1.

// For calculating the base 10 exponent, we need to process the number as if
// it has style E, so here we calculate the precision we'll use in that case.
const size_t exp_precision = init_precision - 1;

FloatToString<T> float_converter(static_cast<T>(float_bits));

// Here we would subtract 1 to account for the fact that block 0 counts as a
// positive block, but the loop below accounts for this by starting with
// subtracting 1 from cur_block.
int cur_block;

if (exponent < 0) {
cur_block = -float_converter.zero_blocks_after_point();
} else {
cur_block = float_converter.get_positive_blocks();
}

BlockInt digits = 0;

// 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) {
// This loop finds the first non-zero block.
while (digits == 0) {
--cur_block;
digits = float_converter.get_block(cur_block);
}
} else {
// In the case of 0.0, then it's always decimal format. If we don't have alt
// form then the trailing zeroes are trimmed to make "0", else the precision
// is 1 less than specified by the user.
FormatSection new_conv = to_conv;
if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
// This is a style F conversion, making the precision P - 1 - X, but since
// this is for the number 0, X (the base 10 exponent) is always 0.
new_conv.precision = init_precision - 1;
} else {
new_conv.precision = 0;
}
return convert_float_decimal_typed<T>(writer, new_conv, float_bits);
}

size_t block_width = 0;
{
// TODO: Find a better way to calculate the number of digits in the
// initial block and exponent.
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);
block_width = int_to_str.size();
}

size_t digits_checked = 0;
// TODO: look into unifying trailing_zeroes and trailing_nines. The number can
// end in a nine or a zero, but not both.
size_t trailing_zeroes = 0;
size_t trailing_nines = 0;

base_10_exp = (cur_block * BLOCK_SIZE) + (block_width - 1);

// If the first block is not also the last block
if (block_width <= exp_precision + 1) {
char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);

for (size_t i = 0; i < block_width; ++i) {
if (int_to_str[i] == '9') {
++trailing_nines;
trailing_zeroes = 0;
} else if (int_to_str[i] == '0') {
++trailing_zeroes;
trailing_nines = 0;
} else {
trailing_nines = 0;
trailing_zeroes = 0;
}
}
digits_checked += block_width;
--cur_block;
}

// Handle middle blocks
for (; digits_checked + BLOCK_SIZE < exp_precision + 1; --cur_block) {
digits = float_converter.get_block(cur_block);
digits_checked += BLOCK_SIZE;
if (digits == MAX_BLOCK) {
trailing_nines += 9;
trailing_zeroes = 0;
} else if (digits == 0) {
trailing_zeroes += 9;
trailing_nines = 0;
} else {
// The block is neither all nines nor all zeroes, so we need to figure out
// what it ends with.
trailing_nines = 0;
trailing_zeroes = 0;
BlockInt copy_of_digits = digits;
int cur_last_digit = copy_of_digits % 10;
// We only care if it ends in nines or zeroes.
while (copy_of_digits > 0 &&
(cur_last_digit == 9 || cur_last_digit == 0)) {
// If the next digit is not the same as the previous one, then there are
// no more contiguous trailing digits.
if ((copy_of_digits % 10) != cur_last_digit) {
break;
}
if (cur_last_digit == 9) {
++trailing_nines;
} else if (cur_last_digit == 0) {
++trailing_zeroes;
} else {
break;
}
copy_of_digits /= 10;
}
}
}

// Handle the last block

digits = float_converter.get_block(cur_block);

size_t last_block_size = BLOCK_SIZE;

char buf[IntegerToString::dec_bufsize<intmax_t>()];
auto int_to_str = *IntegerToString::dec(digits, buf);

int implicit_leading_zeroes = BLOCK_SIZE - int_to_str.size();

// if the last block is also the first block, then ignore leading zeroes.
if (digits_checked == 0) {
last_block_size = int_to_str.size();
implicit_leading_zeroes = 0;
} else {
// If the block is not the maximum size, that means it has leading
// zeroes, and zeroes are not nines.
if (implicit_leading_zeroes > 0) {
trailing_nines = 0;
}

// But leading zeroes are zeroes (that could be trailing).
trailing_zeroes += implicit_leading_zeroes;
}

int digits_requested = (exp_precision + 1) - digits_checked;

int digits_to_check = digits_requested - implicit_leading_zeroes;
if (digits_to_check < 0) {
digits_to_check = 0;
}

// Check the upper digits of this block.
for (int i = 0; i < digits_to_check; ++i) {
if (int_to_str[i] == '9') {
++trailing_nines;
trailing_zeroes = 0;
} else if (int_to_str[i] == '0') {
++trailing_zeroes;
trailing_nines = 0;
} else {
trailing_nines = 0;
trailing_zeroes = 0;
}
}

// Find the digit after the lowest digit that we'll actually print to
// determine the rounding.
const uint32_t maximum = exp_precision + 1 - digits_checked;
uint32_t last_digit = 0;
for (uint32_t k = 0; k < last_block_size - maximum; ++k) {
last_digit = digits % 10;
digits /= 10;
}

// If the last block we read doesn't have the digit after the end of what
// we'll print, then we need to read the next block to get that digit.
if (maximum == last_block_size) {
BlockInt extra_block = float_converter.get_block(cur_block - 1);
last_digit = extra_block / ((MAX_BLOCK / 10) + 1);
}

// TODO: unify this code across the three float conversions.
RoundDirection round;
// Is m * 10^(additionalDigits + 1) / 2^(-exponent) integer?
const int32_t requiredTwos =
-exponent - MANT_WIDTH - static_cast<int32_t>(exp_precision) - 1;
// TODO: rename this variable to remove confusion with trailing_zeroes
const bool trailingZeros =
requiredTwos <= 0 ||
(requiredTwos < 60 &&
multiple_of_power_of_2(float_bits.get_explicit_mantissa(),
static_cast<uint32_t>(requiredTwos)));
switch (fputil::get_round()) {
case FE_TONEAREST:
// Round to nearest, if it's exactly halfway then round to even.
if (last_digit != 5) {
round = last_digit > 5 ? RoundDirection::Up : RoundDirection::Down;
} else {
round = trailingZeros ? RoundDirection::Even : RoundDirection::Up;
}
break;
case FE_DOWNWARD:
if (is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
}
break;
case FE_UPWARD:
if (!is_negative && (!trailingZeros || last_digit > 0)) {
round = RoundDirection::Up;
} else {
round = RoundDirection::Down;
}
round = is_negative ? RoundDirection::Down : RoundDirection::Up;
break;
case FE_TOWARDZERO:
round = RoundDirection::Down;
break;
}

bool round_up;
if (round == RoundDirection::Up) {
round_up = true;
} else if (round == RoundDirection::Down) {
round_up = false;
} else {
// RoundDirection is even, so check the extra digit.
uint32_t low_digit = digits % 10;
round_up = (low_digit % 2) != 0;
}

digits_checked += digits_requested;
// assert(digits_checked == init_precision);
// At this point we should have checked all the digits requested by the
// precision. We may increment this number 1 more if we round up all of the
// digits, but at this point in the code digits_checked should always equal
// init_precision.

if (round_up) {
// If all the digits that would be printed are nines, then rounding up means
// that the base 10 exponent is one higher and all those nines turn to
// zeroes (e.g. 999 -> 1000).
if (trailing_nines == init_precision) {
++base_10_exp;
trailing_zeroes = digits_checked;
++digits_checked;
} else {
// If there are trailing nines, they turn into trailing zeroes when
// they're rounded up.
if (trailing_nines > 0) {
trailing_zeroes += trailing_nines;
} else if (trailing_zeroes > 0) {
// If there are trailing zeroes, then the last digit will be rounded up
// to a 1 so they aren't trailing anymore.
trailing_zeroes = 0;
}
}
}

// if P > X >= -4, the conversion is with style f (or F) and precision equals
// P - (X + 1).
if (static_cast<int>(init_precision) > base_10_exp && base_10_exp >= -4) {
FormatSection new_conv = to_conv;
const size_t conv_precision = init_precision - (base_10_exp + 1);

if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
new_conv.precision = conv_precision;
} else {
// If alt form isn't set, then we need to determine the number of trailing
// zeroes and set the precision such that they are removed.
int trimmed_precision =
digits_checked - (base_10_exp + 1) - trailing_zeroes;
if (trimmed_precision < 0) {
trimmed_precision = 0;
}
new_conv.precision =
(static_cast<size_t>(trimmed_precision) > conv_precision)
? conv_precision
: trimmed_precision;
}

return convert_float_decimal_typed<T>(writer, new_conv, float_bits);
} else {
// otherwise, the conversion is with style e (or E) and precision equals
// P - 1
const size_t conv_precision = init_precision - 1;
FormatSection new_conv = to_conv;
if ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0) {
new_conv.precision = conv_precision;
} else {
// If alt form isn't set, then we need to determine the number of trailing
// zeroes and set the precision such that they are removed.
int trimmed_precision = digits_checked - 1 - trailing_zeroes;
if (trimmed_precision < 0) {
trimmed_precision = 0;
}
new_conv.precision =
(static_cast<size_t>(trimmed_precision) > conv_precision)
? conv_precision
: trimmed_precision;
}
return convert_float_dec_exp_typed<T>(writer, new_conv, float_bits);
}
}

// TODO: unify the float converters to remove the duplicated checks for inf/nan.
LIBC_INLINE int convert_float_decimal(Writer *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
Expand Down Expand Up @@ -834,6 +1172,26 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
return convert_inf_nan(writer, to_conv);
}

LIBC_INLINE int convert_float_dec_auto(Writer *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<long double> float_bits(float_raw);
if (!float_bits.is_inf_or_nan()) {
return convert_float_dec_auto_typed<long double>(writer, to_conv,
float_bits);
}
} else {
fputil::FPBits<double>::UIntType float_raw = to_conv.conv_val_raw;
fputil::FPBits<double> float_bits(float_raw);
if (!float_bits.is_inf_or_nan()) {
return convert_float_dec_auto_typed<double>(writer, to_conv, float_bits);
}
}

return convert_inf_nan(writer, to_conv);
}

} // namespace printf_core
} // namespace __llvm_libc

Expand Down
636 changes: 636 additions & 0 deletions libc/test/src/stdio/sprintf_test.cpp

Large diffs are not rendered by default.