From af5d8004fc42b56ceb8204d9911aea2be600c879 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 19 Feb 2022 07:14:18 -0800 Subject: [PATCH 1/5] Limit Dragonbox to supported FP formats --- include/fmt/format-inl.h | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 4f0d92a0..67399cf6 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -2208,7 +2208,13 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, if (specs.fallback) return snprintf_float(value, precision, specs, buf); - if (!is_constant_evaluated() && precision < 0) { + int exp = 0; + bool use_dragon = true; + if (!is_fast_float()) { + // Use floor because 0.9 = 9e-1. + exp = static_cast(std::floor(std::log10(value))); + if (fixed) adjust_precision(precision, exp + 1); + } else if (!is_constant_evaluated() && precision < 0) { // Use Dragonbox for the shortest format. if (specs.binary32) { auto dec = dragonbox::to_decimal(static_cast(value)); @@ -2218,11 +2224,7 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, auto dec = dragonbox::to_decimal(static_cast(value)); write(buffer_appender(buf), dec.significand); return dec.exponent; - } - - int exp = 0; - bool use_dragon = true; - if (is_fast_float()) { + } else { // Use Grisu + Dragon4 for the given precision: // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf. const int min_exp = -60; // alpha in Grisu. @@ -2241,10 +2243,6 @@ FMT_HEADER_ONLY_CONSTEXPR20 int format_float(Float value, int precision, exp += handler.size - cached_exp10 - 1; precision = handler.precision; } - } else { - // Use floor because 0.9 = 9e-1. - exp = static_cast(std::floor(std::log10(value))); - if (fixed) adjust_precision(precision, exp + 1); } if (use_dragon) { auto f = fp(); From 1319719a5ee916dbdafc1173b2ae3ec1cd1ffa2e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 19 Feb 2022 07:57:43 -0800 Subject: [PATCH 2/5] Add underlying_t --- include/fmt/core.h | 15 ++++++++------- include/fmt/format.h | 8 +++----- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 5d458b7c..dbefe110 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -314,6 +314,8 @@ template using remove_cvref_t = typename std::remove_cv>::type; template struct type_identity { using type = T; }; template using type_identity_t = typename type_identity::type; +template +using underlying_t = typename std::underlying_type::type; struct monostate { constexpr monostate() {} @@ -1416,8 +1418,8 @@ template struct arg_mapper { !has_fallback_formatter::value)> FMT_CONSTEXPR FMT_INLINE auto map(const T& val) -> decltype(std::declval().map( - static_cast::type>(val))) { - return map(static_cast::type>(val)); + static_cast>(val))) { + return map(static_cast>(val)); } template constexpr bool is_ascii_letter(Char c) { // Converts a character to ASCII. Returns a number > 127 on conversion failure. template ::value)> -constexpr auto to_ascii(Char value) -> Char { - return value; +constexpr auto to_ascii(Char c) -> Char { + return c; } template ::value)> -constexpr auto to_ascii(Char value) -> - typename std::underlying_type::type { - return value; +constexpr auto to_ascii(Char c) -> underlying_t { + return c; } template diff --git a/include/fmt/format.h b/include/fmt/format.h index 996cc999..dcd1d58a 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2354,8 +2354,7 @@ template < type::custom_type, FMT_ENABLE_IF(check)> FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { - return write( - out, static_cast::type>(value)); + return write(out, static_cast>(value)); } template auto ptr(const std::shared_ptr& p) -> const void* { \endrst */ template -constexpr auto underlying(Enum e) noexcept -> - typename std::underlying_type::type { - return static_cast::type>(e); +constexpr auto underlying(Enum e) noexcept -> underlying_t { + return static_cast>(e); } #ifdef __cpp_lib_byte From aad44f28396b8cf9722d74027b94840820f7c3dc Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 19 Feb 2022 08:03:42 -0800 Subject: [PATCH 3/5] Add fmt::enums::format_as --- include/fmt/format.h | 7 +++++++ test/format-test.cc | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/include/fmt/format.h b/include/fmt/format.h index dcd1d58a..1db4528d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -2891,6 +2891,13 @@ constexpr auto underlying(Enum e) noexcept -> underlying_t { return static_cast>(e); } +namespace enums { +template ::value)> +constexpr auto format_as(Enum e) noexcept -> underlying_t { + return static_cast>(e); +} +} + #ifdef __cpp_lib_byte inline auto format_as(std::byte b) -> unsigned char { return underlying(b); } FMT_FORMAT_AS(std::byte, unsigned char); diff --git a/test/format-test.cc b/test/format-test.cc index c7a468ad..3b540ab8 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1459,8 +1459,14 @@ TEST(format_test, write_uintptr_fallback) { enum class color { red, green, blue }; +namespace test_ns { +enum class color { red, green, blue }; +using fmt::enums::format_as; +} // namespace test_ns + TEST(format_test, format_enum_class) { EXPECT_EQ(fmt::format("{}", fmt::underlying(color::red)), "0"); + EXPECT_EQ(fmt::format("{}", test_ns::color::red), "0"); } TEST(format_test, format_string) { From ea3d326c636a8bb22cec94ebc75273982548143a Mon Sep 17 00:00:00 2001 From: Vladislav Shchapov Date: Sat, 19 Feb 2022 23:52:33 +0500 Subject: [PATCH 4/5] Fix clang -Wliteral-range warning (#2779) --- test/format-test.cc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/format-test.cc b/test/format-test.cc index 3b540ab8..35aee406 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -948,8 +948,10 @@ TEST(format_test, precision) { EXPECT_THAT(outputs, testing::Contains(fmt::format("{:.838A}", -2.14001164E+38))); - auto ld = 8.43821965335442234493E-4933L; - EXPECT_EQ(fmt::format("{:.0}", ld), ld != 0 ? "8e-4933" : "0"); + if (std::numeric_limits::digits == 64) { + auto ld = (std::numeric_limits::min)(); + EXPECT_EQ(fmt::format("{:.0}", ld), "3e-4932"); + } EXPECT_EQ("123.", fmt::format("{:#.0f}", 123.0)); EXPECT_EQ("1.23", fmt::format("{:.02f}", 1.234)); From cbc59ca893f35fcdd82438ef816d16a512d9c7f5 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 20 Feb 2022 08:12:19 -0800 Subject: [PATCH 5/5] Clear moved from memory buffer --- include/fmt/format.h | 3 ++- test/format-test.cc | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 1db4528d..52daed7d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -775,6 +775,7 @@ class basic_memory_buffer final : public detail::buffer { // Set pointer to the inline array so that delete is not called // when deallocating. other.set(other.store_, 0); + other.clear(); } this->resize(size); } @@ -2896,7 +2897,7 @@ template ::value)> constexpr auto format_as(Enum e) noexcept -> underlying_t { return static_cast>(e); } -} +} // namespace enums #ifdef __cpp_lib_byte inline auto format_as(std::byte b) -> unsigned char { return underlying(b); } diff --git a/test/format-test.cc b/test/format-test.cc index 35aee406..96b5f1ae 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -250,8 +250,9 @@ TEST(memory_buffer_test, move_ctor_dynamic_buffer) { buffer.push_back('a'); basic_memory_buffer buffer2(std::move(buffer)); // Move should rip the guts of the first buffer. - EXPECT_EQ(inline_buffer_ptr, &buffer[0]); - EXPECT_EQ("testa", std::string(&buffer2[0], buffer2.size())); + EXPECT_EQ(&buffer[0], inline_buffer_ptr); + EXPECT_EQ(buffer.size(), 0); + EXPECT_EQ(std::string(&buffer2[0], buffer2.size()), "testa"); EXPECT_GT(buffer2.capacity(), 4u); }