From 29db6bd91f9aa59a1a2c8eb1615837532c5118e8 Mon Sep 17 00:00:00 2001 From: gawain Date: Fri, 1 Mar 2019 20:32:05 +0100 Subject: [PATCH] Add support for '%' type to output floating point values as a percentage. This helps with compatibility with Python's format strings. --- include/fmt/format-inl.h | 9 ++++++++- include/fmt/format.h | 24 +++++++++++++++++++++--- test/format-test.cc | 2 +- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 8f2fe1c9..7ca73d12 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -656,7 +656,14 @@ void sprintf_format(Double value, internal::buffer& buf, *format_ptr++ = '*'; } if (std::is_same::value) *format_ptr++ = 'L'; - char type = spec.type ? spec.type : 'g'; + + char type = spec.type; + + if (type == '%') { + type = 'f'; + } else if (type == 0) { + type = 'g'; + } #if FMT_MSC_VER if (type == 'F') { // MSVC's printf doesn't support 'F'. diff --git a/include/fmt/format.h b/include/fmt/format.h index 44584c10..c4e5958d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1274,6 +1274,9 @@ FMT_CONSTEXPR void handle_float_type_spec(char spec, Handler&& handler) { case 'F': handler.on_fixed(); break; + case '%': + handler.on_percent(); + break; case 'a': case 'A': handler.on_hex(); @@ -1338,6 +1341,7 @@ class float_type_checker : private ErrorHandler { FMT_CONSTEXPR void on_general() {} FMT_CONSTEXPR void on_exp() {} FMT_CONSTEXPR void on_fixed() {} + FMT_CONSTEXPR void on_percent() {} FMT_CONSTEXPR void on_hex() {} FMT_CONSTEXPR void on_error() { @@ -2808,10 +2812,11 @@ template class basic_writer { struct float_spec_handler { char type; - bool upper; - bool fixed; + bool upper{false}; + bool fixed{false}; + bool as_percentage{false}; - explicit float_spec_handler(char t) : type(t), upper(false), fixed(false) {} + explicit float_spec_handler(char t) : type(t) {} void on_general() { if (type == 'G') upper = true; @@ -2826,6 +2831,11 @@ struct float_spec_handler { if (type == 'F') upper = true; } + void on_percent() { + fixed = true; + as_percentage = true; + } + void on_hex() { if (type == 'A') upper = true; } @@ -2869,11 +2879,19 @@ void basic_writer::write_double(T value, const format_specs& spec) { memory_buffer buffer; int exp = 0; int precision = spec.has_precision() || !spec.type ? spec.precision : 6; + + if (handler.as_percentage) value *= 100.; + bool use_grisu = fmt::internal::use_grisu() && (!spec.type || handler.fixed) && internal::grisu2_format(static_cast(value), buffer, precision, handler.fixed, exp); if (!use_grisu) internal::sprintf_format(value, buffer, spec); + + if (handler.as_percentage) { + buffer.push_back('%'); + --exp; // Adjust decimal place position. + } align_spec as = spec; if (spec.align() == ALIGN_NUMERIC) { if (sign) { diff --git a/test/format-test.cc b/test/format-test.cc index 822557b7..45c3432e 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1443,7 +1443,7 @@ TEST(FormatterTest, FormatFloat) { } TEST(FormatterTest, FormatDouble) { - check_unknown_types(1.2, "eEfFgGaA", "double"); + check_unknown_types(1.2, "eEfFgGaA%", "double"); EXPECT_EQ("0.0", format("{:}", 0.0)); EXPECT_EQ("0.000000", format("{:f}", 0.0)); EXPECT_EQ("0", format("{:g}", 0.0));