From e9c0b2d69effc3187449f88df16df8fce748bc21 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 8 Oct 2020 17:18:16 -0700 Subject: [PATCH] Merge write_float overloads --- include/fmt/format.h | 184 +++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 84 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 515851ee..d0179a7e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1731,96 +1731,112 @@ struct big_decimal_fp { int exp; }; -template -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; - // 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 - 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(output_exp, out); - } - - int exp = fp.exp + fp.significand_size; - if (fp.significand_size <= exp) { - // 1234e7 -> 12340000000[.0+] - 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; - if (num_zeros <= 0) { - if (specs.format != float_format::fixed) *out++ = zero; - return out; - } -#ifdef FMT_FUZZ - if (num_zeros > 5000) - throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); -#endif - out = std::fill_n(out, num_zeros, zero); - } - } else if (exp > 0) { - // 1234e-2 -> 12.34[0+] - out = copy_str(digits, digits + exp, out); - if (!specs.showpoint) { - 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 + fp.significand_size, out); - // Add trailing zeros. - 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 (fp.significand_size == 0 && specs.precision >= 0 && - specs.precision < num_zeros) { - num_zeros = specs.precision; - } - 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 + fp.significand_size, out); - } - } - return out; -} - // The number is given as v = significand * pow(10, exp). template -OutputIt write_float(OutputIt out, const buffer& significand, int exp, +OutputIt write_float(OutputIt out, const buffer& significand, int fpexp, 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(), fp, fspecs, decimal_point).count(); - size += fspecs.sign ? 1 : 0; + static_cast(significand.size()), fpexp}; + const char* digits = fp.significand; + const Char zero = static_cast('0'); + char sign = data::signs[fspecs.sign]; + int size = fp.significand_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, fp, fspecs, decimal_point); - }); + + int output_exp = fp.exp + fp.significand_size - 1; + auto use_exp_format = [=]() { + if (fspecs.format == float_format::exp) return true; + if (fspecs.format != float_format::general) return false; + // Use the fixed notation if the exponent is in [exp_lower, exp_upper), + // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. + const int exp_lower = -4, exp_upper = 16; + return output_exp < exp_lower || + output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + }; + if (use_exp_format()) { + int num_zeros = 0; + if (fspecs.showpoint) { + num_zeros = fspecs.precision - fp.significand_size; + if (num_zeros < 0) num_zeros = 0; + } else if (fp.significand_size == 1) { + decimal_point = Char(); + } + + auto abs_output_exp = static_cast(output_exp); + if (output_exp < 0) abs_output_exp = 0 - abs_output_exp; + int exp_digits = 2; + if (abs_output_exp >= 100) exp_digits = abs_output_exp >= 1000 ? 4 : 3; + + size += (decimal_point ? 1 : 0) + num_zeros + 2 + exp_digits; + char exp_char = fspecs.upper ? 'E' : 'e'; + return write_padded( + out, specs, to_unsigned(size), [=](iterator it) { + if (sign) *it++ = static_cast(sign); + // Insert a decimal point after the first digit and add an exponent. + *it++ = static_cast(*digits); + if (decimal_point) *it++ = decimal_point; + it = copy_str(digits + 1, digits + fp.significand_size, it); + if (num_zeros > 0) it = std::fill_n(it, num_zeros, zero); + *it++ = static_cast(exp_char); + return write_exponent(output_exp, it); + }); + } + + int exp = fp.exp + fp.significand_size; + if (fp.exp >= 0) { + // 1234e5 -> 123400000[.0+] + size += fp.exp; + int num_zeros = fspecs.precision - exp; +#ifdef FMT_FUZZ + if (num_zeros > 5000) + throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); +#endif + if (fspecs.showpoint) { + if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 1; + if (num_zeros > 0) size += num_zeros; + } + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (sign) *it++ = static_cast(sign); + it = copy_str(digits, digits + fp.significand_size, it); + it = std::fill_n(it, fp.exp, zero); + if (!fspecs.showpoint) return it; + *it++ = decimal_point; + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } else if (exp > 0) { + // 1234e-2 -> 12.34[0+] + int num_zeros = + fspecs.showpoint ? fspecs.precision - fp.significand_size : 0; + size += 1 + (num_zeros > 0 ? num_zeros : 0); + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (sign) *it++ = static_cast(sign); + it = copy_str(digits, digits + exp, it); + *it++ = decimal_point; + it = copy_str(digits + exp, digits + fp.significand_size, it); + // Add trailing zeros. + return num_zeros > 0 ? std::fill_n(it, num_zeros, zero) : it; + }); + } + // 1234e-6 -> 0.001234 + int num_zeros = -exp; + if (fp.significand_size == 0 && fspecs.precision >= 0 && + fspecs.precision < num_zeros) { + num_zeros = fspecs.precision; + } + size += 2 + num_zeros; + return write_padded( + out, specs, to_unsigned(size), [&](iterator it) { + if (sign) *it++ = static_cast(sign); + *it++ = zero; + if (num_zeros == 0 && fp.significand_size == 0 && !fspecs.showpoint) + return it; + *it++ = decimal_point; + it = std::fill_n(it, num_zeros, zero); + return copy_str(digits, digits + fp.significand_size, it); + }); } template