diff --git a/include/fmt/format.h b/include/fmt/format.h index 2b7a933f..b3e3fb86 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -3254,7 +3254,13 @@ FMT_CONSTEXPR20 inline void format_dragon(basic_fp value, if (!upper) upper = &lower; bool shortest = num_digits < 0; if ((flags & dragon::fixup) != 0) { - if (add_compare(numerator, *upper, denominator) + even <= 0) { + // In the shortest mode the upper boundary is included because the output + // may round up to the next power of 10. With a given number of digits, + // rounding happens after digit generation, so only the value is compared. + bool too_high = + shortest ? add_compare(numerator, *upper, denominator) + even <= 0 + : compare(numerator, denominator) < 0; + if (too_high) { --exp10; numerator *= 10; if (num_digits < 0) { diff --git a/test/format-test.cc b/test/format-test.cc index c61d2ce4..19f1fd52 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1283,6 +1283,16 @@ TEST(format_test, precision) { EXPECT_EQ( fmt::format("{:0g}", std::numeric_limits::denorm_min()), "3.6452e-4951"); + + // Values just below a power of 10 where the initial exponent estimate is + // one too high. + ld = std::ldexp(static_cast(0x83126e978d4fdf3bULL), -73); + EXPECT_EQ(fmt::format("{:.20e}", ld), "9.99999999999999999958e-04"); + ld = std::ldexp(static_cast(0xe596b7b0c643c719ULL), 79); + EXPECT_EQ(fmt::format("{:.0f}", ld), + "9999999999999999999741184793924429452148736"); + EXPECT_EQ(fmt::format("{:.19e}", ld), "9.9999999999999999997e+42"); + EXPECT_EQ(fmt::format("{:.21g}", ld), "9.99999999999999999974e+42"); } EXPECT_EQ(fmt::format("{:#.0f}", 123.0), "123.");