diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h index 65fefdb113044..35bbb823e8383 100644 --- a/libc/src/stdio/printf_core/float_dec_converter.h +++ b/libc/src/stdio/printf_core/float_dec_converter.h @@ -965,15 +965,6 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer, 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; @@ -983,6 +974,19 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer, digits_to_check = 0; } + // 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). We take the + // minimum of the leading zeroes and digits requested because if there are + // more requested digits than leading zeroes we shouldn't count those. + trailing_zeroes += + (implicit_leading_zeroes > digits_requested ? digits_requested + : implicit_leading_zeroes); + // Check the upper digits of this block. for (int i = 0; i < digits_to_check; ++i) { if (int_to_str[i] == '9') { @@ -1103,6 +1107,24 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer, } 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. + + /* + Here's a diagram of an example: + + printf("%.15g", 22.25); + + +--- init_precision = 15 + | + +-------------------+ + | | + | ++--- trimmed_precision = 2 + | || | + 22.250000000000000000 + || | | + ++ +--------------+ + | | + base_10_exp + 1 = 2 --+ +--- trailing_zeroes = 11 + */ int trimmed_precision = digits_checked - (base_10_exp + 1) - trailing_zeroes; if (trimmed_precision < 0) { diff --git a/libc/test/src/stdio/sprintf_test.cpp b/libc/test/src/stdio/sprintf_test.cpp index 7214233220a1f..8fc4997b0268b 100644 --- a/libc/test/src/stdio/sprintf_test.cpp +++ b/libc/test/src/stdio/sprintf_test.cpp @@ -2437,6 +2437,10 @@ TEST_F(LlvmLibcSPrintfTest, FloatAutoConv) { written = __llvm_libc::sprintf(buff, "%.3g", 1256.0); ASSERT_STREQ_LEN(written, buff, "1.26e+03"); + // Found through large scale testing. + written = __llvm_libc::sprintf(buff, "%.15g", 22.25); + ASSERT_STREQ_LEN(written, buff, "22.25"); + // Subnormal Precision Tests written = __llvm_libc::sprintf(buff, "%.310g", 0x1.0p-1022);