diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 9e34e2ae..582c6193 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -342,6 +342,7 @@ template const char basic_data::background_color[] = "\x1b[48;2;"; template const char basic_data::reset_color[] = "\x1b[0m"; template const wchar_t basic_data::wreset_color[] = L"\x1b[0m"; +template const char basic_data::signs[] = {0, '-', '+', ' '}; template struct bits { static FMT_CONSTEXPR_DECL const int value = diff --git a/include/fmt/format.h b/include/fmt/format.h index 5b42b20a..fc980fb2 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -670,6 +670,7 @@ template struct FMT_EXTERN_TEMPLATE_API basic_data { static const char background_color[]; static const char reset_color[5]; static const wchar_t wreset_color[5]; + static const char signs[]; }; FMT_EXTERN template struct basic_data; @@ -1042,6 +1043,7 @@ template It write_exponent(int exp, It it) { struct gen_digits_params { int num_digits; + sign_t sign : 3; bool fixed; bool upper; bool trailing_zeros; @@ -1308,6 +1310,41 @@ void arg_map::init(const basic_format_args& args) { } } +template class grisu_writer { + private: + buffer& digits_; + size_t size_; + int exp_; + gen_digits_params params_; + Char decimal_point_; + + public: + grisu_writer(buffer& digits, int exp, + const internal::gen_digits_params& params, Char decimal_point) + : digits_(digits), + exp_(exp), + params_(params), + decimal_point_(decimal_point) { + int num_digits = static_cast(digits.size()); + int full_exp = num_digits + exp - 1; + int precision = params.num_digits > 0 ? params.num_digits : 16; + params_.fixed |= full_exp >= -4 && full_exp < precision; + auto it = grisu_prettify(digits.data(), num_digits, exp, + counting_iterator(), params_, '\0'); + size_ = it.count(); + } + + size_t size() const { return size_ + (params_.sign ? 1 : 0); } + size_t width() const { return size(); } + + template void operator()(It&& it) { + if (params_.sign) *it++ = static_cast(data::signs[params_.sign]); + int num_digits = static_cast(digits_.size()); + it = grisu_prettify(digits_.data(), num_digits, exp_, it, params_, + decimal_point_); + } +}; + // This template provides operations for formatting and writing data into a // character range. template class basic_writer { @@ -1510,7 +1547,7 @@ template class basic_writer { enum { inf_size = 3 }; // This is an enum to workaround a bug in MSVC. struct inf_or_nan_writer { - char sign; + sign_t sign; bool as_percentage; const char* str; @@ -1521,7 +1558,7 @@ template class basic_writer { size_t width() const { return size(); } template void operator()(It&& it) const { - if (sign) *it++ = static_cast(sign); + if (sign) *it++ = static_cast(data::signs[sign]); it = internal::copy_str( str, str + static_cast(inf_size), it); if (as_percentage) *it++ = static_cast('%'); @@ -1529,7 +1566,7 @@ template class basic_writer { }; struct double_writer { - char sign; + sign_t sign; internal::buffer& buffer; char* decimal_point_pos; char_type decimal_point; @@ -1538,7 +1575,7 @@ template class basic_writer { size_t width() const { return size(); } template void operator()(It&& it) { - if (sign) *it++ = static_cast(sign); + if (sign) *it++ = static_cast(data::signs[sign]); auto begin = buffer.begin(); if (decimal_point_pos) { it = internal::copy_str(begin, decimal_point_pos, it); @@ -1549,45 +1586,6 @@ template class basic_writer { } }; - class grisu_writer { - private: - internal::buffer& digits_; - size_t size_; - char sign_; - int exp_; - internal::gen_digits_params params_; - char_type decimal_point_; - - public: - grisu_writer(char sign, internal::buffer& digits, int exp, - const internal::gen_digits_params& params, - char_type decimal_point) - : digits_(digits), - sign_(sign), - exp_(exp), - params_(params), - decimal_point_(decimal_point) { - int num_digits = static_cast(digits.size()); - int full_exp = num_digits + exp - 1; - int precision = params.num_digits > 0 ? params.num_digits : 16; - params_.fixed |= full_exp >= -4 && full_exp < precision; - auto it = internal::grisu_prettify( - digits.data(), num_digits, exp, internal::counting_iterator(), - params_, '.'); - size_ = it.count(); - } - - size_t size() const { return size_ + (sign_ ? 1 : 0); } - size_t width() const { return size(); } - - template void operator()(It&& it) { - if (sign_) *it++ = static_cast(sign_); - int num_digits = static_cast(digits_.size()); - it = internal::grisu_prettify(digits_.data(), num_digits, exp_, - it, params_, decimal_point_); - } - }; - template struct str_writer { const Char* s; size_t size_; @@ -2780,18 +2778,15 @@ void internal::basic_writer::write_fp(T value, const format_specs& specs) { // Check type. float_spec_handler handler(static_cast(specs.type)); - internal::handle_float_type_spec(handler.type, handler); + handle_float_type_spec(handler.type, handler); - char sign = 0; + auto sign = specs.sign; // Use signbit instead of value < 0 since the latter is always false for NaN. if (std::signbit(value)) { - sign = '-'; + sign = sign::minus; value = -value; - } else if (specs.sign != sign::none) { - if (specs.sign == sign::plus) - sign = '+'; - else if (specs.sign == sign::space) - sign = ' '; + } else if (sign == sign::minus) { + sign = sign::none; } if (!std::isfinite(value)) { @@ -2809,17 +2804,15 @@ void internal::basic_writer::write_fp(T value, int exp = 0; int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; unsigned options = 0; - if (handler.fixed) options |= internal::grisu_options::fixed; - if (sizeof(value) == sizeof(float)) - options |= internal::grisu_options::binary32; - bool use_grisu = USE_GRISU && - (specs.type != 'a' && specs.type != 'A' && - specs.type != 'e' && specs.type != 'E') && - internal::grisu_format(static_cast(value), buffer, - precision, options, exp); + if (handler.fixed) options |= grisu_options::fixed; + if (sizeof(value) == sizeof(float)) options |= grisu_options::binary32; + bool use_grisu = + USE_GRISU && + (specs.type != 'a' && specs.type != 'A' && specs.type != 'e' && + specs.type != 'E') && + grisu_format(static_cast(value), buffer, precision, options, exp); char* decimal_point_pos = nullptr; - if (!use_grisu) - decimal_point_pos = internal::sprintf_format(value, buffer, specs); + if (!use_grisu) decimal_point_pos = sprintf_format(value, buffer, specs); if (handler.as_percentage) { buffer.push_back('%'); @@ -2829,8 +2822,8 @@ void internal::basic_writer::write_fp(T value, if (specs.align == align::numeric) { if (sign) { auto&& it = reserve(1); - *it++ = static_cast(sign); - sign = 0; + *it++ = static_cast(data::signs[sign]); + sign = sign::none; if (as.width) --as.width; } as.align = align::right; @@ -2841,12 +2834,14 @@ void internal::basic_writer::write_fp(T value, ? internal::decimal_point(locale_) : static_cast('.'); if (use_grisu) { - auto params = internal::gen_digits_params(); + auto params = gen_digits_params(); + params.sign = sign; params.fixed = handler.fixed; params.num_digits = precision; params.trailing_zeros = (precision != 0 && (handler.fixed || !specs.type)) || specs.alt; - write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point)); + write_padded(as, + grisu_writer(buffer, exp, params, decimal_point)); } else { write_padded(as, double_writer{sign, buffer, decimal_point_pos, decimal_point});