From 7eddbfed533e1d8584b06298f36d610b5905fc06 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 7 Oct 2020 13:27:46 -0700 Subject: [PATCH] Cleanup exponent handling in write_float --- include/fmt/format.h | 64 ++++++++++++++++++++++++++------------------ test/format-test.cc | 2 ++ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index b949b5ca..515851ee 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1724,37 +1724,46 @@ OutputIt write_nonfinite(OutputIt out, bool isinf, }); } +// A decimal floating-point number significand * pow(10, exp). +struct big_decimal_fp { + const char* significand; + int significand_size; + int exp; +}; + template -OutputIt write_float(OutputIt out, const buffer& significand, int exp, - float_specs specs, Char decimal_point) { - const char* digits = significand.data(); - int num_digits = static_cast(significand.size()); - exp += num_digits; +OutputIt write_float(OutputIt out, const big_decimal_fp& fp, float_specs specs, + Char decimal_point) { + const char* digits = fp.significand; const Char zero = static_cast('0'); + int output_exp = fp.exp + fp.significand_size - 1; auto use_exp_format = [=]() { if (specs.format == float_format::exp) return true; if (specs.format != float_format::general) return false; - const int exp_threshold = -4; // Prefer 0.0001 to 1e-04. - return exp - 1 < exp_threshold || - exp - 1 >= (specs.precision > 0 ? specs.precision : 17); + // Format numbers with the exponent in [exp_lower, exp_upper) using + // the fixed notation, e.g. prefer 0.0001 to 1e-04. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); }; if (use_exp_format()) { // Insert a decimal point after the first digit and add an exponent. *out++ = static_cast(*digits); - int num_zeros = specs.precision - num_digits; - if (num_digits > 1 || specs.showpoint) *out++ = decimal_point; - out = copy_str(digits + 1, digits + num_digits, out); + int num_zeros = specs.precision - fp.significand_size; + if (fp.significand_size > 1 || specs.showpoint) *out++ = decimal_point; + out = copy_str(digits + 1, digits + fp.significand_size, out); if (num_zeros > 0 && specs.showpoint) out = std::fill_n(out, num_zeros, zero); *out++ = static_cast(specs.upper ? 'E' : 'e'); - return write_exponent(exp - 1, out); + return write_exponent(output_exp, out); } - if (num_digits <= exp) { + int exp = fp.exp + fp.significand_size; + if (fp.significand_size <= exp) { // 1234e7 -> 12340000000[.0+] - out = copy_str(digits, digits + num_digits, out); - out = std::fill_n(out, exp - num_digits, zero); + out = copy_str(digits, digits + fp.significand_size, out); + out = std::fill_n(out, exp - fp.significand_size, zero); if (specs.showpoint) { *out++ = decimal_point; int num_zeros = specs.precision - exp; @@ -1772,24 +1781,26 @@ OutputIt write_float(OutputIt out, const buffer& significand, int exp, // 1234e-2 -> 12.34[0+] out = copy_str(digits, digits + exp, out); if (!specs.showpoint) { - if (num_digits != exp) *out++ = decimal_point; - return copy_str(digits + exp, digits + num_digits, out); + if (fp.significand_size != exp) *out++ = decimal_point; + return copy_str(digits + exp, digits + fp.significand_size, out); } *out++ = decimal_point; - out = copy_str(digits + exp, digits + num_digits, out); + out = copy_str(digits + exp, digits + fp.significand_size, out); // Add trailing zeros. - if (specs.precision > num_digits) - out = std::fill_n(out, specs.precision - num_digits, zero); + if (specs.precision > fp.significand_size) + out = std::fill_n(out, specs.precision - fp.significand_size, zero); } else { // 1234e-6 -> 0.001234 *out++ = zero; int num_zeros = -exp; - if (num_digits == 0 && specs.precision >= 0 && specs.precision < num_zeros) + if (fp.significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { num_zeros = specs.precision; - if (num_zeros != 0 || num_digits != 0 || specs.showpoint) { + } + if (num_zeros != 0 || fp.significand_size != 0 || specs.showpoint) { *out++ = decimal_point; out = std::fill_n(out, num_zeros, zero); - out = copy_str(digits, digits + num_digits, out); + out = copy_str(digits, digits + fp.significand_size, out); } } return out; @@ -1800,14 +1811,15 @@ template OutputIt write_float(OutputIt out, const buffer& significand, int exp, const basic_format_specs& specs, float_specs fspecs, Char decimal_point) { + auto fp = big_decimal_fp{significand.data(), + static_cast(significand.size()), exp}; auto size = - write_float(counting_iterator(), significand, exp, fspecs, decimal_point) - .count(); + write_float(counting_iterator(), fp, fspecs, decimal_point).count(); size += fspecs.sign ? 1 : 0; using iterator = remove_reference_t; return write_padded(out, specs, size, [&](iterator it) { if (fspecs.sign) *it++ = static_cast(data::signs[fspecs.sign]); - return write_float(it, significand, exp, fspecs, decimal_point); + return write_float(it, fp, fspecs, decimal_point); }); } diff --git a/test/format-test.cc b/test/format-test.cc index d5d3f6c9..bf563e17 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1294,6 +1294,8 @@ TEST(FormatterTest, PrecisionRounding) { TEST(FormatterTest, PrettifyFloat) { EXPECT_EQ("0.0001", fmt::format("{}", 1e-4)); EXPECT_EQ("1e-05", fmt::format("{}", 1e-5)); + EXPECT_EQ("1000000000000000", fmt::format("{}", 1e15)); + EXPECT_EQ("1e+16", fmt::format("{}", 1e16)); EXPECT_EQ("9.999e-05", fmt::format("{}", 9.999e-5)); EXPECT_EQ("10000000000", fmt::format("{}", 1e10)); EXPECT_EQ("100000000000", fmt::format("{}", 1e11));