Skip to content

Commit 4209e41

Browse files
[libc] Fix printf long double bugs (#166474)
Found in testing against abseil. Two bugs were found: 1) SHIFT_AMOUNT in float_converter<long double> would sometimes be negative causing an underflow when passed as the amount to left shift by for BigInt. 2) is_lowest_block had an off-by-one because it was adding 1 to the block index. Both are fixed and there are new tests to catch regressions.
1 parent 02f5a1a commit 4209e41

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

libc/src/__support/float_to_string.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,11 @@ template <> class FloatToString<long double> {
700700

701701
const int SHIFT_AMOUNT = FLOAT_AS_INT_WIDTH + exponent;
702702
static_assert(EXTRA_INT_WIDTH >= sizeof(long double) * 8);
703-
float_as_fixed <<= SHIFT_AMOUNT;
703+
if (SHIFT_AMOUNT > 0) {
704+
float_as_fixed <<= SHIFT_AMOUNT;
705+
} else {
706+
float_as_fixed >>= -SHIFT_AMOUNT;
707+
}
704708

705709
// If there are still digits above the decimal point, handle those.
706710
if (cpp::countl_zero(float_as_fixed) <
@@ -769,7 +773,7 @@ template <> class FloatToString<long double> {
769773
// The decimal representation of 2**(-i) will have exactly i digits after
770774
// the decimal point.
771775
const int num_requested_digits =
772-
static_cast<int>((negative_block_index + 1) * BLOCK_SIZE);
776+
static_cast<int>(negative_block_index * BLOCK_SIZE);
773777

774778
return num_requested_digits > -exponent;
775779
}

libc/test/src/stdio/sprintf_test.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1537,6 +1537,14 @@ TEST(LlvmLibcSPrintfTest, FloatDecimalLongDoubleConv) {
15371537
#if defined(LIBC_TYPES_LONG_DOUBLE_IS_X86_FLOAT80)
15381538

15391539
#ifndef LIBC_COPT_FLOAT_TO_STR_REDUCED_PRECISION
1540+
written = LIBC_NAMESPACE::sprintf(
1541+
buff, "%.75Lf",
1542+
0.0833333333333333333355920878593448009041821933351457118988037109375L);
1543+
ASSERT_STREQ_LEN(written, buff,
1544+
"0."
1545+
"08333333333333333333559208785934480090418219333514571189880"
1546+
"3710937500000000");
1547+
15401548
written = LIBC_NAMESPACE::sprintf(buff, "%Lf", 1e100L);
15411549
ASSERT_STREQ_LEN(written, buff,
15421550
"99999999999999999996693535322073426194986990198284960792713"
@@ -2976,6 +2984,10 @@ TEST(LlvmLibcSPrintfTest, FloatAutoLongDoubleConv) {
29762984
written = LIBC_NAMESPACE::sprintf(buff, "%Lg", 0xf.fffffffffffffffp+16380L);
29772985
ASSERT_STREQ_LEN(written, buff, "1.18973e+4932");
29782986

2987+
// Minimum normal
2988+
written = LIBC_NAMESPACE::sprintf(buff, "%Lg", 3.36210314311209350626E-4932L);
2989+
ASSERT_STREQ_LEN(written, buff, "3.3621e-4932");
2990+
29792991
written = LIBC_NAMESPACE::sprintf(buff, "%Lg", 0xa.aaaaaaaaaaaaaabp-7L);
29802992
ASSERT_STREQ_LEN(written, buff, "0.0833333");
29812993

0 commit comments

Comments
 (0)