From 1d3e3d8c0466dedbb26547576e25a8f36f013520 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Thu, 4 Jul 2019 15:15:14 -0700 Subject: [PATCH] Make the 'n' format specifier work with grisu disabled --- include/fmt/format-inl.h | 10 ++++++---- include/fmt/format.h | 27 +++++++++++++++++++-------- src/format.cc | 10 +++++----- test/locale-test.cc | 7 +++++++ 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 59defa2d..c1120e4c 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -753,8 +753,8 @@ FMT_API bool grisu_format(Double value, buffer& buf, int precision, } template -void sprintf_format(Double value, internal::buffer& buf, - core_format_specs spec) { +char* sprintf_format(Double value, internal::buffer& buf, + core_format_specs spec) { // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. FMT_ASSERT(buf.capacity() != 0, "empty buffer"); @@ -774,7 +774,7 @@ void sprintf_format(Double value, internal::buffer& buf, if (type == '%') type = 'f'; - else if (type == 0) + else if (type == 0 || type == 'n') type = 'g'; #if FMT_MSC_VER if (type == 'F') { @@ -787,6 +787,7 @@ void sprintf_format(Double value, internal::buffer& buf, // Format using snprintf. char* start = nullptr; + char* decimal_point_pos = nullptr; for (;;) { std::size_t buffer_size = buf.capacity(); start = &buf[0]; @@ -801,7 +802,7 @@ void sprintf_format(Double value, internal::buffer& buf, if (spec.type != 'a' && spec.type != 'A') { while (p < end && *p >= '0' && *p <= '9') ++p; if (p < end && *p != 'e' && *p != 'E') { - if (*p != '.') *p = '.'; + decimal_point_pos = p; if (!spec.type) { // Keep only one trailing zero after the decimal point. ++p; @@ -826,6 +827,7 @@ void sprintf_format(Double value, internal::buffer& buf, buf.reserve(buf.capacity() + 1); } } + return decimal_point_pos; } } // namespace internal diff --git a/include/fmt/format.h b/include/fmt/format.h index 38f791cd..b99a5644 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1052,7 +1052,7 @@ It grisu_prettify(const char* digits, int size, int exp, It it, } template -void sprintf_format(Double, internal::buffer&, core_format_specs); +char* sprintf_format(Double, internal::buffer&, core_format_specs); template FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) { @@ -1442,13 +1442,21 @@ template class basic_writer { struct double_writer { char sign; internal::buffer& buffer; + char* decimal_point_pos; + char_type decimal_point; size_t size() const { return buffer.size() + (sign ? 1 : 0); } size_t width() const { return size(); } template void operator()(It&& it) { if (sign) *it++ = static_cast(sign); - it = internal::copy_str(buffer.begin(), buffer.end(), it); + auto begin = buffer.begin(); + if (decimal_point_pos) { + it = internal::copy_str(begin, decimal_point_pos, it); + *it++ = decimal_point; + begin = decimal_point_pos + 1; + } + it = internal::copy_str(begin, buffer.end(), it); } }; @@ -1595,7 +1603,8 @@ template class basic_writer { } // Formats a floating-point number (double or long double). - template void write_double(T value, const format_specs& spec); + template ()> + void write_double(T value, const format_specs& spec); /** Writes a character to the buffer. */ void write(char value) { @@ -2697,7 +2706,7 @@ struct float_spec_handler { }; template -template +template void internal::basic_writer::write_double(T value, const format_specs& spec) { // Check type. @@ -2728,12 +2737,14 @@ void internal::basic_writer::write_double(T value, int exp = 0; int precision = spec.has_precision() || !spec.type ? spec.precision : 6; unsigned options = handler.fixed ? internal::grisu_options::fixed : 0; - bool use_grisu = fmt::internal::use_grisu() && + bool use_grisu = USE_GRISU && (spec.type != 'a' && spec.type != 'A' && spec.type != 'e' && spec.type != 'E') && internal::grisu_format(static_cast(value), buffer, precision, options, exp); - if (!use_grisu) internal::sprintf_format(value, buffer, spec); + char* decimal_point_pos = nullptr; + if (!use_grisu) + decimal_point_pos = internal::sprintf_format(value, buffer, spec); if (handler.as_percentage) { buffer.push_back('%'); @@ -2762,8 +2773,8 @@ void internal::basic_writer::write_double(T value, spec.has(HASH_FLAG); write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point)); } else { - // TODO: set decimal point - write_padded(as, double_writer{sign, buffer}); + write_padded(as, + double_writer{sign, buffer, decimal_point_pos, decimal_point}); } } diff --git a/src/format.cc b/src/format.cc index e0c0574d..8e73d31b 100644 --- a/src/format.cc +++ b/src/format.cc @@ -35,11 +35,11 @@ template FMT_API std::string internal::vformat( template FMT_API format_context::iterator internal::vformat_to( internal::buffer&, string_view, basic_format_args); -template FMT_API void internal::sprintf_format(double, internal::buffer&, - core_format_specs); -template FMT_API void internal::sprintf_format(long double, - internal::buffer&, - core_format_specs); +template FMT_API char* internal::sprintf_format(double, internal::buffer&, + core_format_specs); +template FMT_API char* internal::sprintf_format(long double, + internal::buffer&, + core_format_specs); // Explicit instantiations for wchar_t. diff --git a/test/locale-test.cc b/test/locale-test.cc index 2e7a9a2a..91508e77 100644 --- a/test/locale-test.cc +++ b/test/locale-test.cc @@ -18,6 +18,13 @@ template struct numpunct : std::numpunct { TEST(LocaleTest, DoubleDecimalPoint) { std::locale loc(std::locale(), new numpunct()); EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23)); + // Test with Grisu disabled. + fmt::memory_buffer buf; + fmt::internal::writer w(buf, fmt::internal::locale_ref(loc)); + auto specs = fmt::format_specs(); + specs.type = 'n'; + w.write_double(1.23, specs); + EXPECT_EQ(fmt::to_string(buf), "1?23"); } TEST(LocaleTest, Format) {