From c3be0f593dbe5b56efb4e3bf7305b29b60696469 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 27 Nov 2019 07:32:29 -0800 Subject: [PATCH] Refactor floating-point formatting --- include/fmt/format-inl.h | 2 +- include/fmt/format.h | 68 +++++++++++++++++++--------------------- test/format | 2 +- 3 files changed, 35 insertions(+), 37 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 6bee711a..646e328f 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -1128,7 +1128,7 @@ int snprintf_float(T value, int precision, float_specs specs, char format[max_format_size]; char* format_ptr = format; *format_ptr++ = '%'; - if (specs.alt) *format_ptr++ = '#'; + if (specs.trailing_zeros) *format_ptr++ = '#'; if (precision >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; diff --git a/include/fmt/format.h b/include/fmt/format.h index 041de3f9..c2c31d8d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1076,7 +1076,6 @@ struct float_specs { bool upper : 1; bool locale : 1; bool percent : 1; - bool alt : 1; bool binary32 : 1; bool use_grisu : 1; bool trailing_zeros : 1; @@ -1110,32 +1109,32 @@ template class float_writer { int num_digits_; int exp_; size_t size_; - float_specs params_; + float_specs specs_; Char decimal_point_; template It prettify(It it) const { // pow(10, full_exp - 1) <= v <= pow(10, full_exp). int full_exp = num_digits_ + exp_; - if (params_.format == float_format::exp) { + if (specs_.format == float_format::exp) { // Insert a decimal point after the first digit and add an exponent. *it++ = static_cast(*digits_); if (num_digits_ > 1) *it++ = decimal_point_; it = copy_str(digits_ + 1, digits_ + num_digits_, it); - int num_zeros = params_.precision - num_digits_; - if (num_zeros > 0 && params_.trailing_zeros) + int num_zeros = specs_.precision - num_digits_; + if (num_zeros > 0 && specs_.trailing_zeros) it = std::fill_n(it, num_zeros, static_cast('0')); - *it++ = static_cast(params_.upper ? 'E' : 'e'); + *it++ = static_cast(specs_.upper ? 'E' : 'e'); return write_exponent(full_exp - 1, it); } if (num_digits_ <= full_exp) { // 1234e7 -> 12340000000[.0+] it = copy_str(digits_, digits_ + num_digits_, it); it = std::fill_n(it, full_exp - num_digits_, static_cast('0')); - if (params_.trailing_zeros) { + if (specs_.trailing_zeros) { *it++ = decimal_point_; - int num_zeros = params_.precision - full_exp; + int num_zeros = specs_.precision - full_exp; if (num_zeros <= 0) { - if (params_.format != float_format::fixed) + if (specs_.format != float_format::fixed) *it++ = static_cast('0'); return it; } @@ -1148,7 +1147,7 @@ template class float_writer { } else if (full_exp > 0) { // 1234e-2 -> 12.34[0+] it = copy_str(digits_, digits_ + full_exp, it); - if (!params_.trailing_zeros) { + if (!specs_.trailing_zeros) { // Remove trailing zeros. int num_digits = num_digits_; while (num_digits > full_exp && digits_[num_digits - 1] == '0') @@ -1158,19 +1157,19 @@ template class float_writer { } *it++ = decimal_point_; it = copy_str(digits_ + full_exp, digits_ + num_digits_, it); - if (params_.precision > num_digits_) { + if (specs_.precision > num_digits_) { // Add trailing zeros. - int num_zeros = params_.precision - num_digits_; + int num_zeros = specs_.precision - num_digits_; it = std::fill_n(it, num_zeros, static_cast('0')); } } else { // 1234e-6 -> 0.001234 *it++ = static_cast('0'); int num_zeros = -full_exp; - if (params_.precision >= 0 && params_.precision < num_zeros) - num_zeros = params_.precision; + if (specs_.precision >= 0 && specs_.precision < num_zeros) + num_zeros = specs_.precision; int num_digits = num_digits_; - if (!params_.trailing_zeros) + if (!specs_.trailing_zeros) while (num_digits > 0 && digits_[num_digits - 1] == '0') --num_digits; if (num_zeros != 0 || num_digits != 0) { *it++ = decimal_point_; @@ -1187,13 +1186,13 @@ template class float_writer { : digits_(digits), num_digits_(num_digits), exp_(exp), - params_(specs), + specs_(specs), decimal_point_(decimal_point) { int full_exp = num_digits + exp - 1; int precision = specs.precision > 0 ? specs.precision : 16; - if (params_.format == float_format::general && + if (specs_.format == float_format::general && !(full_exp >= -4 && full_exp < precision)) { - params_.format = float_format::exp; + specs_.format = float_format::exp; } size_ = prettify(counting_iterator()).count(); size_ += specs.sign ? 1 : 0; @@ -1203,7 +1202,7 @@ template class float_writer { size_t width() const { return size(); } template void operator()(It&& it) { - if (params_.sign) *it++ = static_cast(data::signs[params_.sign]); + if (specs_.sign) *it++ = static_cast(data::signs[specs_.sign]); it = prettify(it); } }; @@ -1245,15 +1244,19 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { } } -template -FMT_CONSTEXPR float_specs parse_float_type_spec(char spec, - ErrorHandler&& eh = {}) { +template +FMT_CONSTEXPR float_specs parse_float_type_spec( + const basic_format_specs& specs, ErrorHandler&& eh = {}) { auto result = float_specs(); - switch (spec) { + result.trailing_zeros = specs.alt; + switch (specs.type) { + case 0: + result.format = float_format::general; + result.trailing_zeros |= specs.precision != 0; + break; case 'G': result.upper = true; FMT_FALLTHROUGH; - case 0: case 'g': result.format = float_format::general; break; @@ -1262,12 +1265,14 @@ FMT_CONSTEXPR float_specs parse_float_type_spec(char spec, FMT_FALLTHROUGH; case 'e': result.format = float_format::exp; + result.trailing_zeros |= specs.precision != 0; break; case 'F': result.upper = true; FMT_FALLTHROUGH; case 'f': result.format = float_format::fixed; + result.trailing_zeros |= specs.precision != 0; break; #if FMT_DEPRECATED_PERCENT case '%': @@ -1378,7 +1383,7 @@ void arg_map::init(const basic_format_args& args) { } } -template struct inf_or_nan_writer { +template struct nonfinite_writer { sign_t sign; const char* str; static constexpr size_t str_size = 3; @@ -1691,7 +1696,7 @@ template class basic_writer { template ::value)> void write(T value, format_specs specs = {}) { - float_specs fspecs = parse_float_type_spec(specs.type); + float_specs fspecs = parse_float_type_spec(specs); fspecs.sign = specs.sign; if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. fspecs.sign = sign::minus; @@ -1703,8 +1708,7 @@ template class basic_writer { if (!std::isfinite(value)) { auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf") : (fspecs.upper ? "NAN" : "nan"); - return write_padded(specs, - inf_or_nan_writer{fspecs.sign, str}); + return write_padded(specs, nonfinite_writer{fspecs.sign, str}); } if (specs.align == align::none) { @@ -1722,18 +1726,12 @@ template class basic_writer { memory_buffer buffer; if (fspecs.format == float_format::hex) { if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]); - fspecs.alt = specs.alt; snprintf_float(promote_float(value), specs.precision, fspecs, buffer); write_padded(specs, str_writer{buffer.data(), buffer.size()}); return; } int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; if (fspecs.format == float_format::exp) ++precision; - fspecs.trailing_zeros = - (precision != 0 && - (!specs.type || fspecs.format == float_format::fixed || - fspecs.format == float_format::exp)) || - specs.alt; if (const_check(std::is_same())) fspecs.binary32 = true; fspecs.use_grisu = use_grisu(); if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100; @@ -2956,7 +2954,7 @@ struct formatter