From 6dcc526d5be660e28cbc89e945f73eaeb10ef4c0 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 26 May 2018 07:02:49 -0700 Subject: [PATCH 01/17] Update release script --- support/manage.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/support/manage.py b/support/manage.py index 6b2ceb09..6bf703fa 100755 --- a/support/manage.py +++ b/support/manage.py @@ -152,7 +152,9 @@ def update_site(env): if os.path.exists(html_dir): shutil.rmtree(html_dir) include_dir = env.fmt_repo.dir - if LooseVersion(version) >= LooseVersion('3.0.0'): + if LooseVersion(version) >= LooseVersion('5.0.0'): + include_dir = os.path.join(include_dir, 'include', 'fmt') + elif LooseVersion(version) >= LooseVersion('3.0.0'): include_dir = os.path.join(include_dir, 'fmt') import build build.build_docs(version, doc_dir=target_doc_dir, From fba352a92ad22cdbb6de53856ccaa90eabb2621e Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 26 May 2018 08:07:45 -0700 Subject: [PATCH 02/17] Don't use UDL templates on Intel C++ compiler (#742) --- include/fmt/format.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index f274586c..0d672085 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -115,7 +115,7 @@ # endif #endif -#if FMT_USE_USER_DEFINED_LITERALS && \ +#if FMT_USE_USER_DEFINED_LITERALS && !defined(FMT_ICC_VERSION) && \ ((FMT_GCC_VERSION >= 600 && __cplusplus >= 201402L) || \ (defined(FMT_CLANG_VERSION) && FMT_CLANG_VERSION >= 304)) # define FMT_UDL_TEMPLATE 1 From e2cd521b8f80180c4df22b998621bf2674d6de5f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 26 May 2018 09:23:09 -0700 Subject: [PATCH 03/17] Fix incorrect call to on_align in '{:}=' (#750) --- include/fmt/format.h | 67 ++++++++++++++++++++++---------------------- test/format-test.cc | 1 + 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 0d672085..3d8fe70e 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1975,41 +1975,42 @@ struct precision_adapter { template FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) { typedef typename std::iterator_traits::value_type char_type; + char_type c = *it; + if (c == '}' || !c) + return it; + // Parse fill and alignment. - if (char_type c = *it) { - alignment align = ALIGN_DEFAULT; - int i = 1; - do { - auto p = it + i; - switch (*p) { - case '<': - align = ALIGN_LEFT; - break; - case '>': - align = ALIGN_RIGHT; - break; - case '=': - align = ALIGN_NUMERIC; - break; - case '^': - align = ALIGN_CENTER; - break; - } - if (align != ALIGN_DEFAULT) { - handler.on_align(align); - if (p != it) { - if (c == '}') break; - if (c == '{') { - handler.on_error("invalid fill character '{'"); - return it; - } - it += 2; - handler.on_fill(c); - } else ++it; + alignment align = ALIGN_DEFAULT; + int i = 1; + do { + auto p = it + i; + switch (*p) { + case '<': + align = ALIGN_LEFT; break; - } - } while (--i >= 0); - } + case '>': + align = ALIGN_RIGHT; + break; + case '=': + align = ALIGN_NUMERIC; + break; + case '^': + align = ALIGN_CENTER; + break; + } + if (align != ALIGN_DEFAULT) { + if (p != it) { + if (c == '{') { + handler.on_error("invalid fill character '{'"); + return it; + } + it += 2; + handler.on_fill(c); + } else ++it; + handler.on_align(align); + break; + } + } while (--i >= 0); // Parse sign. switch (*it) { diff --git a/test/format-test.cc b/test/format-test.cc index 0044390e..a2e647bb 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -435,6 +435,7 @@ TEST(FormatterTest, Fill) { EXPECT_EQ("c****", format("{0:*<5}", 'c')); EXPECT_EQ("abc**", format("{0:*<5}", "abc")); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast(0xface))); + EXPECT_EQ("foo=", format("{:}=", "foo")); } TEST(FormatterTest, PlusSign) { From e282d963fecb72f85d86b0b98858f2614aaa466a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sat, 26 May 2018 09:46:59 -0700 Subject: [PATCH 04/17] Bump version --- include/fmt/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 0986789f..96bbf27c 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -16,7 +16,7 @@ #include // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 50000 +#define FMT_VERSION 50100 #ifdef __has_feature # define FMT_HAS_FEATURE(x) __has_feature(x) From 6a5bb6e26856bc25ee9d1df9b103e6ba5ebb5929 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 27 May 2018 06:20:38 -0700 Subject: [PATCH 05/17] Move Android.mk to support and update --- Android.mk => support/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename Android.mk => support/Android.mk (87%) diff --git a/Android.mk b/support/Android.mk similarity index 87% rename from Android.mk rename to support/Android.mk index 5d862391..84a3e32f 100644 --- a/Android.mk +++ b/support/Android.mk @@ -4,7 +4,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := fmt_static LOCAL_MODULE_FILENAME := libfmt -LOCAL_SRC_FILES := fmt/format.cc +LOCAL_SRC_FILES := ../src/format.cc LOCAL_C_INCLUDES := $(LOCAL_PATH) LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) From 2f257b72915430dfc6bb880fbb8d4d47e12dfd80 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 27 May 2018 08:04:30 -0700 Subject: [PATCH 06/17] Implement normalization and simplify power table --- include/fmt/format-inl.h | 51 +++++++++++++++++---------------------- include/fmt/format.h | 42 ++++++++++++++++++++++++-------- support/compute-powers.py | 4 +-- test/util-test.cc | 45 +++++++++++++++++++++++++--------- 4 files changed, 90 insertions(+), 52 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 223ed516..b8f9a2b0 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -294,35 +294,28 @@ const uint64_t basic_data::POWERS_OF_10_64[] = { // These are generated by support/compute-powers.py. template const uint64_t basic_data::POW10_SIGNIFICANDS[] = { - 0xfa8fd5a0081c0288ull, 0xbaaee17fa23ebf76ull, 0x8b16fb203055ac76ull, - 0xcf42894a5dce35eaull, 0x9a6bb0aa55653b2dull, 0xe61acf033d1a45dfull, - 0xab70fe17c79ac6caull, 0xff77b1fcbebcdc4full, 0xbe5691ef416bd60cull, - 0x8dd01fad907ffc3cull, 0xd3515c2831559a83ull, 0x9d71ac8fada6c9b5ull, - 0xea9c227723ee8bcbull, 0xaecc49914078536dull, 0x823c12795db6ce57ull, - 0xc21094364dfb5637ull, 0x9096ea6f3848984full, 0xd77485cb25823ac7ull, - 0xa086cfcd97bf97f4ull, 0xef340a98172aace5ull, 0xb23867fb2a35b28eull, - 0x84c8d4dfd2c63f3bull, 0xc5dd44271ad3cdbaull, 0x936b9fcebb25c996ull, - 0xdbac6c247d62a584ull, 0xa3ab66580d5fdaf6ull, 0xf3e2f893dec3f126ull, - 0xb5b5ada8aaff80b8ull, 0x87625f056c7c4a8bull, 0xc9bcff6034c13053ull, - 0x964e858c91ba2655ull, 0xdff9772470297ebdull, 0xa6dfbd9fb8e5b88full, - 0xf8a95fcf88747d94ull, 0xb94470938fa89bcfull, 0x8a08f0f8bf0f156bull, - 0xcdb02555653131b6ull, 0x993fe2c6d07b7facull, 0xe45c10c42a2b3b06ull, - 0xaa242499697392d3ull, 0xfd87b5f28300ca0eull, 0xbce5086492111aebull, - 0x8cbccc096f5088ccull, 0xd1b71758e219652cull, 0x9c40000000000000ull, - 0xe8d4a51000000000ull, 0xad78ebc5ac620000ull, 0x813f3978f8940984ull, - 0xc097ce7bc90715b3ull, 0x8f7e32ce7bea5c70ull, 0xd5d238a4abe98068ull, - 0x9f4f2726179a2245ull, 0xed63a231d4c4fb27ull, 0xb0de65388cc8ada8ull, - 0x83c7088e1aab65dbull, 0xc45d1df942711d9aull, 0x924d692ca61be758ull, - 0xda01ee641a708deaull, 0xa26da3999aef774aull, 0xf209787bb47d6b85ull, - 0xb454e4a179dd1877ull, 0x865b86925b9bc5c2ull, 0xc83553c5c8965d3dull, - 0x952ab45cfa97a0b3ull, 0xde469fbd99a05fe3ull, 0xa59bc234db398c25ull, - 0xf6c69a72a3989f5cull, 0xb7dcbf5354e9beceull, 0x88fcf317f22241e2ull, - 0xcc20ce9bd35c78a5ull, 0x98165af37b2153dfull, 0xe2a0b5dc971f303aull, - 0xa8d9d1535ce3b396ull, 0xfb9b7cd9a4a7443cull, 0xbb764c4ca7a44410ull, - 0x8bab8eefb6409c1aull, 0xd01fef10a657842cull, 0x9b10a4e5e9913129ull, - 0xe7109bfba19c0c9dull, 0xac2820d9623bf429ull, 0x80444b5e7aa7cf85ull, - 0xbf21e44003acdd2dull, 0x8e679c2f5e44ff8full, 0xd433179d9c8cb841ull, - 0x9e19db92b4e31ba9ull, 0xeb96bf6ebadf77d9ull, 0xaf87023b9bf0ee6bull + 0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea, + 0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, + 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c, 0xd3515c2831559a83, 0x9d71ac8fada6c9b5, + 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637, + 0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5, + 0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b, 0xc5dd44271ad3cdba, 0x936b9fcebb25c996, + 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8, + 0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd, + 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94, 0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, + 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3, + 0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, + 0x9c40000000000000, 0xe8d4a51000000000, 0xad78ebc5ac620000, 0x813f3978f8940984, + 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245, + 0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a, + 0x924d692ca61be758, 0xda01ee641a708dea, 0xa26da3999aef774a, 0xf209787bb47d6b85, + 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3, + 0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, + 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5, 0x98165af37b2153df, 0xe2a0b5dc971f303a, + 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a, + 0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, + 0x80444b5e7aa7cf85, 0xbf21e44003acdd2d, 0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, + 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b }; // Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding diff --git a/include/fmt/format.h b/include/fmt/format.h index 3d8fe70e..590ce83d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -258,7 +258,17 @@ inline dummy_int isnan(...) { return dummy_int(); } inline dummy_int _isnan(...) { return dummy_int(); } // A handmade floating-point number f * pow(2, e). -struct fp { +class fp { + private: + // All sizes are in bits. + static constexpr int char_size = std::numeric_limits::digits; + // Subtract 1 to account for an implicit most significant bit in the + // normalized form. + static constexpr int double_significand_size = + std::numeric_limits::digits - 1; + static constexpr uint64_t implicit_bit = 1ull << double_significand_size; + + public: uint64_t f; int e; @@ -270,24 +280,36 @@ struct fp { explicit fp(Double d) { // Assume double is in the format [sign][exponent][significand]. typedef std::numeric_limits limits; - const int double_size = - sizeof(Double) * std::numeric_limits::digits; - // Subtract 1 to account for an implicit most significant bit in the - // normalized form. - const int significand_size = limits::digits - 1; - const int exponent_size = double_size - significand_size - 1; // -1 for sign - const uint64_t implicit_bit = 1ull << significand_size; + const int double_size = sizeof(Double) * char_size; + const int exponent_size = + double_size - double_significand_size - 1; // -1 for sign const uint64_t significand_mask = implicit_bit - 1; const uint64_t exponent_mask = (~0ull >> 1) & ~significand_mask; const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1; auto u = bit_cast(d); - auto biased_e = (u & exponent_mask) >> significand_size; + auto biased_e = (u & exponent_mask) >> double_significand_size; f = u & significand_mask; if (biased_e != 0) f += implicit_bit; else biased_e = 1; // Subnormals use biased exponent 1 (min exponent). - e = biased_e - exponent_bias - significand_size; + e = biased_e - exponent_bias - double_significand_size; + } + + // Normalizes the value converted from double and multiplied by (1 << SHIFT). + template + void normalize() { + // Handle subnormals. + auto shifted_implicit_bit = implicit_bit << SHIFT; + while ((f & shifted_implicit_bit) == 0) { + f <<= 1; + --e; + } + auto fp_significand_size = sizeof(f) * char_size; + // Subtract 1 to account for hidden bit. + auto offset = fp_significand_size - double_significand_size - SHIFT - 1; + f <<= offset; + e -= offset; } }; diff --git a/support/compute-powers.py b/support/compute-powers.py index a68197d2..76edef66 100755 --- a/support/compute-powers.py +++ b/support/compute-powers.py @@ -39,9 +39,9 @@ for i, exp in enumerate(range(min_exponent, max_exponent + 1, step)): print('Significands:', end='') for i, fp in enumerate(powers): - if i % 3 == 0: + if i % 4 == 0: print(end='\n ') - print(' {:0<#16x}ull'.format(fp.f, ), end=',') + print(' {:0<#16x}'.format(fp.f, ), end=',') print('\n\nExponents:', end='') for i, fp in enumerate(powers): diff --git a/test/util-test.cc b/test/util-test.cc index 7c7b6079..62fba708 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -881,17 +881,40 @@ TEST(UtilTest, ParseNonnegativeInt) { fmt::format_error, "number is too big"); } -TEST(UtilTest, FPSubtract) { - auto r = fp(123, 1) - fp(102, 1); - EXPECT_EQ(r.f, 21u); - EXPECT_EQ(r.e, 1); +template +void test_construct_from_double() { + fmt::print("warning: double is not IEC559, skipping FP tests\n"); } -TEST(UtilTest, FPMultiply) { - auto r = fp(123ULL << 32, 4) * fp(56ULL << 32, 7); - EXPECT_EQ(r.f, 123u * 56u); - EXPECT_EQ(r.e, 4 + 7 + 64); - r = fp(123ULL << 32, 4) * fp(567ULL << 31, 8); - EXPECT_EQ(r.f, (123 * 567 + 1u) / 2); - EXPECT_EQ(r.e, 4 + 8 + 64); +template <> +void test_construct_from_double() { + auto v = fp(1.23); + EXPECT_EQ(v.f, 0x13ae147ae147aeu); + EXPECT_EQ(v.e, -52); +} + +TEST(FPTest, ConstructFromDouble) { + test_construct_from_double::is_iec559>(); +} + +TEST(FPTest, Normalize) { + auto v = fp(0xbeef, 42); + v.normalize(); + EXPECT_EQ(0xbeef000000000000, v.f); + EXPECT_EQ(-6, v.e); +} + +TEST(FPTest, Subtract) { + auto v = fp(123, 1) - fp(102, 1); + EXPECT_EQ(v.f, 21u); + EXPECT_EQ(v.e, 1); +} + +TEST(FPTest, Multiply) { + auto v = fp(123ULL << 32, 4) * fp(56ULL << 32, 7); + EXPECT_EQ(v.f, 123u * 56u); + EXPECT_EQ(v.e, 4 + 7 + 64); + v = fp(123ULL << 32, 4) * fp(567ULL << 31, 8); + EXPECT_EQ(v.f, (123 * 567 + 1u) / 2); + EXPECT_EQ(v.e, 4 + 8 + 64); } From 468c243ca81b9eae579af1bd8eadd27ce8d543a5 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 27 May 2018 10:57:26 -0700 Subject: [PATCH 07/17] Add a function to get cached power of 10 --- include/fmt/format-inl.h | 13 +++++++++++++ include/fmt/format.h | 12 +++++------- test/util-test.cc | 12 ++++++++++++ 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index b8f9a2b0..00c97c3f 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -342,6 +342,19 @@ FMT_FUNC fp operator*(fp x, fp y) { uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31); return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64); } + +FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) { + const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) + int index = static_cast(std::ceil( + (min_exponent + fp::fp_significand_size - 1) * one_over_log2_10)); + // Decimal exponent of the first (smallest) cached power of 10. + const int first_dec_exp = -348; + // Difference between two consecutive decimal exponents in cached powers of 10. + const int dec_exp_step = 8; + index = (index - first_dec_exp - 1) / dec_exp_step + 1; + pow10_exponent = first_dec_exp + index * dec_exp_step; + return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]); +} } // namespace internal #if FMT_USE_WINDOWS_H diff --git a/include/fmt/format.h b/include/fmt/format.h index 590ce83d..a9111d4d 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -272,6 +272,8 @@ class fp { uint64_t f; int e; + static constexpr int fp_significand_size = sizeof(f) * char_size; + fp(uint64_t f, int e): f(f), e(e) {} // Constructs fp from an IEEE754 double. It is a template to prevent compile @@ -305,7 +307,6 @@ class fp { f <<= 1; --e; } - auto fp_significand_size = sizeof(f) * char_size; // Subtract 1 to account for hidden bit. auto offset = fp_significand_size - double_significand_size - SHIFT - 1; f <<= offset; @@ -323,12 +324,9 @@ inline fp operator-(fp x, fp y) { // with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized. fp operator*(fp x, fp y); -// Compute k such that its cached power c_k = c_k.f * pow(2, c_k.e) satisfies -// min_exponent <= c_k.e + e <= min_exponent + 3. -inline int compute_cached_power_index(int e, int min_exponent) { - const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) - return static_cast(std::ceil((min_exponent - e + 63) * one_over_log2_10)); -} +// Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its +// (binary) exponent satisfies min_exponent <= c_k.e <= min_exponent + 3. +fp get_cached_power(int min_exponent, int &pow10_exponent); template typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) { diff --git a/test/util-test.cc b/test/util-test.cc index 62fba708..101f3e01 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -918,3 +918,15 @@ TEST(FPTest, Multiply) { EXPECT_EQ(v.f, (123 * 567 + 1u) / 2); EXPECT_EQ(v.e, 4 + 8 + 64); } + +TEST(FPTest, GetCachedPower) { + typedef std::numeric_limits limits; + for (auto exp = limits::min_exponent; exp <= limits::max_exponent; ++exp) { + int dec_exp = 0; + auto fp = fmt::internal::get_cached_power(exp, dec_exp); + EXPECT_LE(exp, fp.e); + int dec_exp_step = 8; + EXPECT_LE(fp.e, exp + dec_exp_step * log2(10)); + EXPECT_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e)); + } +} From 5c32aa411c5915b17a915c650c6975fc2c81271a Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 27 May 2018 11:18:27 -0700 Subject: [PATCH 08/17] Workaround a bug in MSVC --- include/fmt/format.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index a9111d4d..0f2baab4 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -260,6 +260,8 @@ inline dummy_int _isnan(...) { return dummy_int(); } // A handmade floating-point number f * pow(2, e). class fp { private: + typedef uint64_t significand_type; + // All sizes are in bits. static constexpr int char_size = std::numeric_limits::digits; // Subtract 1 to account for an implicit most significant bit in the @@ -269,10 +271,11 @@ class fp { static constexpr uint64_t implicit_bit = 1ull << double_significand_size; public: - uint64_t f; + significand_type f; int e; - static constexpr int fp_significand_size = sizeof(f) * char_size; + static constexpr int fp_significand_size = + sizeof(significand_type) * char_size; fp(uint64_t f, int e): f(f), e(e) {} From 4027557958311e629426ca0e916cbeefd43ef899 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 27 May 2018 11:38:53 -0700 Subject: [PATCH 09/17] Fix tests on 64-bit MSVC --- test/util-test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/util-test.cc b/test/util-test.cc index 101f3e01..5a0515d3 100644 --- a/test/util-test.cc +++ b/test/util-test.cc @@ -927,6 +927,6 @@ TEST(FPTest, GetCachedPower) { EXPECT_LE(exp, fp.e); int dec_exp_step = 8; EXPECT_LE(fp.e, exp + dec_exp_step * log2(10)); - EXPECT_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e)); + EXPECT_DOUBLE_EQ(pow(10, dec_exp), ldexp(fp.f, fp.e)); } } From 4e4b8570e533ce1cb4e12a7f6cf54099550ea96f Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 28 May 2018 11:25:07 -0700 Subject: [PATCH 10/17] Implement simple version of Grisu --- include/fmt/format-inl.h | 2 +- include/fmt/format.h | 158 +++++++++++++++++++++++++-------------- 2 files changed, 103 insertions(+), 57 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 00c97c3f..f02c257e 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -346,7 +346,7 @@ FMT_FUNC fp operator*(fp x, fp y) { FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) { const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10) int index = static_cast(std::ceil( - (min_exponent + fp::fp_significand_size - 1) * one_over_log2_10)); + (min_exponent + fp::significand_size - 1) * one_over_log2_10)); // Decimal exponent of the first (smallest) cached power of 10. const int first_dec_exp = -348; // Difference between two consecutive decimal exponents in cached powers of 10. diff --git a/include/fmt/format.h b/include/fmt/format.h index 0f2baab4..46f06082 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -132,10 +132,15 @@ # endif #endif -#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || FMT_MSC_VER >= 1600 +#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \ + FMT_MSC_VER >= 1600 # define FMT_USE_TRAILING_RETURN 1 #endif +#ifndef FMT_USE_GRISU +# define FMT_USE_GRISU 0 +#endif + // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #ifndef _MSC_VER @@ -274,8 +279,7 @@ class fp { significand_type f; int e; - static constexpr int fp_significand_size = - sizeof(significand_type) * char_size; + static constexpr int significand_size = sizeof(significand_type) * char_size; fp(uint64_t f, int e): f(f), e(e) {} @@ -311,7 +315,7 @@ class fp { --e; } // Subtract 1 to account for hidden bit. - auto offset = fp_significand_size - double_significand_size - SHIFT - 1; + auto offset = significand_size - double_significand_size - SHIFT - 1; f <<= offset; e -= offset; } @@ -395,6 +399,38 @@ FMT_BEGIN_NAMESPACE template class basic_writer; +template +class output_range { + private: + OutputIt it_; + + // Unused yet. + typedef void sentinel; + sentinel end() const; + + public: + typedef OutputIt iterator; + typedef T value_type; + + explicit output_range(OutputIt it): it_(it) {} + OutputIt begin() const { return it_; } +}; + +// A range where begin() returns back_insert_iterator. +template +class back_insert_range: + public output_range> { + typedef output_range> base; + public: + typedef typename Container::value_type value_type; + + back_insert_range(Container &c): base(std::back_inserter(c)) {} + back_insert_range(typename base::iterator it): base(it) {} +}; + +typedef basic_writer> writer; +typedef basic_writer> wwriter; + /** A formatting error such as invalid format string. */ class format_error : public std::runtime_error { public: @@ -2619,6 +2655,10 @@ 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_sprintf(T value, const format_specs &spec, + internal::basic_buffer& buffer, + char sign); template struct str_writer { @@ -2841,7 +2881,60 @@ void basic_writer::write_double(T value, const format_specs &spec) { return write_inf_or_nan(handler.upper ? "INF" : "inf"); basic_memory_buffer buffer; + if (FMT_USE_GRISU && sizeof(T) <= sizeof(double) && + std::numeric_limits::is_iec559) { + internal::fp fp_value(static_cast(value)); + fp_value.normalize(); + // Find a cached power of 10 close to 1 / fp_value. + int dec_exp = 0; + int min_exp = -60; + auto dec_pow = internal::get_cached_power( + min_exp - (fp_value.e + internal::fp::significand_size), dec_exp); + internal::fp product = fp_value * dec_pow; + // Generate output. + internal::fp one(1ull << -product.e, product.e); + uint32_t hi = product.f >> -one.e; + uint64_t f = product.f & (one.f - 1); + typedef back_insert_range> range; + basic_writer w{range(buffer)}; + w.write(hi); + w.write('.'); + for (int i = 0; i < 18; ++i) { + f *= 10; + w.write(static_cast('0' + (f >> -one.e))); + f &= one.f - 1; + } + w.write('e'); + w.write(-dec_exp); + } else { + format_specs normalized_spec(spec); + normalized_spec.type_ = handler.type; + write_double_sprintf(value, normalized_spec, buffer, sign); + } + unsigned n = buffer.size(); + align_spec as = spec; + if (spec.align() == ALIGN_NUMERIC) { + if (sign) { + *reserve(1) = sign; + sign = 0; + if (as.width_) + --as.width_; + } + as.align_ = ALIGN_RIGHT; + } else { + if (spec.align() == ALIGN_DEFAULT) + as.align_ = ALIGN_RIGHT; + if (sign) + ++n; + } + write_padded(n, as, double_writer{n, sign, buffer}); +} +template +template +void basic_writer::write_double_sprintf( + T value, const format_specs &spec, + internal::basic_buffer& buffer, char sign) { unsigned width = spec.width(); if (sign) { buffer.reserve(width > 1u ? width : 1u); @@ -2864,11 +2957,10 @@ void basic_writer::write_double(T value, const format_specs &spec) { } append_float_length(format_ptr, value); - *format_ptr++ = handler.type; + *format_ptr++ = spec.type(); *format_ptr = '\0'; // Format using snprintf. - unsigned n = 0; char_type *start = FMT_NULL; for (;;) { std::size_t buffer_size = buffer.capacity(); @@ -2885,9 +2977,11 @@ void basic_writer::write_double(T value, const format_specs &spec) { int result = internal::char_traits::format_float( start, buffer_size, format, width_for_sprintf, spec.precision(), value); if (result >= 0) { - n = internal::to_unsigned(result); - if (n < buffer.capacity()) + unsigned n = internal::to_unsigned(result); + if (n < buffer.capacity()) { + buffer.resize(n); break; // The buffer is large enough - continue with formatting. + } buffer.reserve(n + 1); } else { // If result is negative we ask to increase the capacity by at least 1, @@ -2895,56 +2989,8 @@ void basic_writer::write_double(T value, const format_specs &spec) { buffer.reserve(buffer.capacity() + 1); } } - align_spec as = spec; - if (spec.align() == ALIGN_NUMERIC) { - if (sign) { - *reserve(1) = sign; - sign = 0; - if (as.width_) - --as.width_; - } - as.align_ = ALIGN_RIGHT; - } else { - if (spec.align() == ALIGN_DEFAULT) - as.align_ = ALIGN_RIGHT; - if (sign) - ++n; - } - write_padded(n, as, double_writer{n, sign, buffer}); } -template -class output_range { - private: - OutputIt it_; - - // Unused yet. - typedef void sentinel; - sentinel end() const; - - public: - typedef OutputIt iterator; - typedef T value_type; - - explicit output_range(OutputIt it): it_(it) {} - OutputIt begin() const { return it_; } -}; - -// A range where begin() returns back_insert_iterator. -template -class back_insert_range: - public output_range> { - typedef output_range> base; - public: - typedef typename Container::value_type value_type; - - back_insert_range(Container &c): base(std::back_inserter(c)) {} - back_insert_range(typename base::iterator it): base(it) {} -}; - -typedef basic_writer> writer; -typedef basic_writer> wwriter; - // Reports a system error without throwing an exception. // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, From 8dc2360b11626c7aaafe33867ae1ce17080625d5 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 28 May 2018 17:21:07 -0700 Subject: [PATCH 11/17] Fix a comment --- include/fmt/format.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 46f06082..3fc2c6f4 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -327,8 +327,8 @@ inline fp operator-(fp x, fp y) { return fp(x.f - y.f, x.e); } -// Computes an fp number r with r.f = x.f * y.f / pow(2, 32) rounded to nearest -// with half-up tie breaking, r.e = x.e + y.e + 32. Result may not be normalized. +// Computes an fp number r with r.f = x.f * y.f / pow(2, 64) rounded to nearest +// with half-up tie breaking, r.e = x.e + y.e + 64. Result may not be normalized. fp operator*(fp x, fp y); // Returns cached power (of 10) c_k = c_k.f * pow(2, c_k.e) such that its From b60a5c5d49978c3f646313f8b75391c2180642e8 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 28 May 2018 20:16:30 -0700 Subject: [PATCH 12/17] Improve floating-point formatting --- include/fmt/format-inl.h | 25 +++++------------- include/fmt/format.h | 54 +++++++++++++++------------------------ src/format.cc | 12 ++++----- support/compute-powers.py | 3 +++ 4 files changed, 36 insertions(+), 58 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index f02c257e..11c9e525 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -232,30 +232,19 @@ FMT_FUNC void system_error::init( namespace internal { template int char_traits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, value) : - FMT_SNPRINTF(buffer, size, format, precision, value); - } + char *buffer, std::size_t size, const char *format, int precision, T value) { return precision < 0 ? - FMT_SNPRINTF(buffer, size, format, width, value) : - FMT_SNPRINTF(buffer, size, format, width, precision, value); + FMT_SNPRINTF(buffer, size, format, value) : + FMT_SNPRINTF(buffer, size, format, precision, value); } template int char_traits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, T value) { - if (width == 0) { - return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, value) : - FMT_SWPRINTF(buffer, size, format, precision, value); - } + wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, + T value) { return precision < 0 ? - FMT_SWPRINTF(buffer, size, format, width, value) : - FMT_SWPRINTF(buffer, size, format, width, precision, value); + FMT_SWPRINTF(buffer, size, format, value) : + FMT_SWPRINTF(buffer, size, format, precision, value); } template diff --git a/include/fmt/format.h b/include/fmt/format.h index 3fc2c6f4..369e8869 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -660,30 +660,30 @@ struct char_traits { // Formats a floating-point number. template FMT_API static int format_float(char *buffer, std::size_t size, - const char *format, unsigned width, int precision, T value); + const char *format, int precision, T value); }; template <> struct char_traits { template FMT_API static int format_float(wchar_t *buffer, std::size_t size, - const wchar_t *format, unsigned width, int precision, T value); + const wchar_t *format, int precision, T value); }; #if FMT_USE_EXTERN_TEMPLATES extern template int char_traits::format_float( - char *buffer, std::size_t size, const char* format, unsigned width, - int precision, double value); + char *buffer, std::size_t size, const char* format, int precision, + double value); extern template int char_traits::format_float( - char *buffer, std::size_t size, const char* format, unsigned width, - int precision, long double value); + char *buffer, std::size_t size, const char* format, int precision, + long double value); extern template int char_traits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, - int precision, double value); + wchar_t *buffer, std::size_t size, const wchar_t* format, int precision, + double value); extern template int char_traits::format_float( - wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, - int precision, long double value); + wchar_t *buffer, std::size_t size, const wchar_t* format, int precision, + long double value); #endif template @@ -2657,8 +2657,7 @@ class basic_writer { void write_double(T value, const format_specs &spec); template void write_double_sprintf(T value, const format_specs &spec, - internal::basic_buffer& buffer, - char sign); + internal::basic_buffer& buffer); template struct str_writer { @@ -2887,7 +2886,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { fp_value.normalize(); // Find a cached power of 10 close to 1 / fp_value. int dec_exp = 0; - int min_exp = -60; + const int min_exp = -60; auto dec_pow = internal::get_cached_power( min_exp - (fp_value.e + internal::fp::significand_size), dec_exp); internal::fp product = fp_value * dec_pow; @@ -2898,8 +2897,10 @@ void basic_writer::write_double(T value, const format_specs &spec) { typedef back_insert_range> range; basic_writer w{range(buffer)}; w.write(hi); + unsigned digits = buffer.size(); w.write('.'); - for (int i = 0; i < 18; ++i) { + const unsigned max_digits = 18; + while (digits++ < max_digits) { f *= 10; w.write(static_cast('0' + (f >> -one.e))); f &= one.f - 1; @@ -2909,7 +2910,7 @@ void basic_writer::write_double(T value, const format_specs &spec) { } else { format_specs normalized_spec(spec); normalized_spec.type_ = handler.type; - write_double_sprintf(value, normalized_spec, buffer, sign); + write_double_sprintf(value, normalized_spec, buffer); } unsigned n = buffer.size(); align_spec as = spec; @@ -2934,23 +2935,17 @@ template template void basic_writer::write_double_sprintf( T value, const format_specs &spec, - internal::basic_buffer& buffer, char sign) { - unsigned width = spec.width(); - if (sign) { - buffer.reserve(width > 1u ? width : 1u); - if (width > 0) - --width; - } + internal::basic_buffer& buffer) { + // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. + FMT_ASSERT(buffer.capacity() != 0, "empty buffer"); // Build format string. enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg char_type format[MAX_FORMAT_SIZE]; char_type *format_ptr = format; *format_ptr++ = '%'; - unsigned width_for_sprintf = width; if (spec.flag(HASH_FLAG)) *format_ptr++ = '#'; - width_for_sprintf = 0; if (spec.precision() >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; @@ -2964,18 +2959,9 @@ void basic_writer::write_double_sprintf( char_type *start = FMT_NULL; for (;;) { std::size_t buffer_size = buffer.capacity(); -#if FMT_MSC_VER - // MSVC's vsnprintf_s doesn't work with zero size, so reserve - // space for at least one extra character to make the size non-zero. - // Note that the buffer's capacity may increase by more than 1. - if (buffer_size == 0) { - buffer.reserve(1); - buffer_size = buffer.capacity(); - } -#endif start = &buffer[0]; int result = internal::char_traits::format_float( - start, buffer_size, format, width_for_sprintf, spec.precision(), value); + start, buffer_size, format, spec.precision(), value); if (result >= 0) { unsigned n = internal::to_unsigned(result); if (n < buffer.capacity()) { diff --git a/src/format.cc b/src/format.cc index 9ebce0ca..111c671e 100644 --- a/src/format.cc +++ b/src/format.cc @@ -21,12 +21,12 @@ template void internal::arg_map::init( const basic_format_args &args); template FMT_API int internal::char_traits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); + char *buffer, std::size_t size, const char *format, int precision, + double value); template FMT_API int internal::char_traits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, long double value); + char *buffer, std::size_t size, const char *format, int precision, + long double value); // Explicit instantiations for wchar_t. @@ -39,9 +39,9 @@ template void internal::arg_map::init( template FMT_API int internal::char_traits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, double value); + int precision, double value); template FMT_API int internal::char_traits::format_float( wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); + int precision, long double value); FMT_END_NAMESPACE diff --git a/support/compute-powers.py b/support/compute-powers.py index 76edef66..967ce8dd 100755 --- a/support/compute-powers.py +++ b/support/compute-powers.py @@ -48,3 +48,6 @@ for i, fp in enumerate(powers): if i % 11 == 0: print(end='\n ') print(' {:5}'.format(fp.e), end=',') + +print('\n\nMax exponent difference:', + max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:])) From 68f0ac82715810282708006ef836b6fdb3385fec Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 3 Jun 2018 08:03:56 +0200 Subject: [PATCH 13/17] Fix a bogus MSVC warning about unreachable code --- include/fmt/format.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/fmt/format.h b/include/fmt/format.h index 369e8869..c4c9fea8 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1727,8 +1727,7 @@ class precision_checker: public function { template FMT_CONSTEXPR typename std::enable_if< !is_integer::value, unsigned long long>::type operator()(T) { - handler_.on_error("precision is not integer"); - return 0; + return handler_.on_error("precision is not integer"), 0; } private: From 81d56638259792812e14dce01f9e3f74174c3cae Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 3 Jun 2018 20:10:17 +0200 Subject: [PATCH 14/17] Fix more bogus MSVC warnings about unreachable code (#748) --- include/fmt/core.h | 4 ++-- include/fmt/format.h | 12 ++++-------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 96bbf27c..612aa195 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -740,8 +740,8 @@ class basic_parse_context : private ErrorHandler { FMT_CONSTEXPR bool check_arg_id(unsigned) { if (next_arg_id_ > 0) { - on_error("cannot switch from automatic to manual argument indexing"); - return false; + return on_error( + "cannot switch from automatic to manual argument indexing"), false; } next_arg_id_ = -1; return true; diff --git a/include/fmt/format.h b/include/fmt/format.h index c4c9fea8..bf125845 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1322,8 +1322,7 @@ template FMT_CONSTEXPR unsigned basic_parse_context::next_arg_id() { if (next_arg_id_ >= 0) return internal::to_unsigned(next_arg_id_++); - on_error("cannot switch from manual to automatic argument indexing"); - return 0; + return on_error("cannot switch from manual to automatic argument indexing"), 0; } struct format_string {}; @@ -1703,8 +1702,7 @@ class width_checker: public function { template FMT_CONSTEXPR typename std::enable_if< !is_integer::value, unsigned long long>::type operator()(T) { - handler_.on_error("width is not integer"); - return 0; + return handler_.on_error("width is not integer"), 0; } private: @@ -2058,10 +2056,8 @@ FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) { } if (align != ALIGN_DEFAULT) { if (p != it) { - if (c == '{') { - handler.on_error("invalid fill character '{'"); - return it; - } + if (c == '{') + return handler.on_error("invalid fill character '{'"), it; it += 2; handler.on_fill(c); } else ++it; From 252f11f85d33b8861f4b2b2409f1bc02f8d3fd89 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 4 Jun 2018 11:59:59 +0200 Subject: [PATCH 15/17] Fix a bogus MSVC warning about unreachable code, take 2 --- include/fmt/core.h | 4 ++-- include/fmt/format.h | 33 +++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 612aa195..96bbf27c 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -740,8 +740,8 @@ class basic_parse_context : private ErrorHandler { FMT_CONSTEXPR bool check_arg_id(unsigned) { if (next_arg_id_ > 0) { - return on_error( - "cannot switch from automatic to manual argument indexing"), false; + on_error("cannot switch from automatic to manual argument indexing"); + return false; } next_arg_id_ = -1; return true; diff --git a/include/fmt/format.h b/include/fmt/format.h index bf125845..e63216ce 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -98,7 +98,23 @@ #ifndef FMT_THROW # if FMT_EXCEPTIONS -# define FMT_THROW(x) throw x +# if FMT_MSC_VER +FMT_BEGIN_NAMESPACE +namespace internal { +template +inline void do_throw(const Exception &x) { + // Silence unreachable code warnings in MSVC because these are nearly + // impossible to fix in a generic code. + volatile bool b = true; + if (b) + throw x; +} +} +FMT_END_NAMESPACE +# define FMT_THROW(x) fmt::internal::do_throw(x) +# else +# define FMT_THROW(x) throw x +# endif # else # define FMT_THROW(x) assert(false) # endif @@ -1322,7 +1338,8 @@ template FMT_CONSTEXPR unsigned basic_parse_context::next_arg_id() { if (next_arg_id_ >= 0) return internal::to_unsigned(next_arg_id_++); - return on_error("cannot switch from manual to automatic argument indexing"), 0; + on_error("cannot switch from manual to automatic argument indexing"); + return 0; } struct format_string {}; @@ -1702,7 +1719,8 @@ class width_checker: public function { template FMT_CONSTEXPR typename std::enable_if< !is_integer::value, unsigned long long>::type operator()(T) { - return handler_.on_error("width is not integer"), 0; + handler_.on_error("width is not integer"); + return 0; } private: @@ -1725,7 +1743,8 @@ class precision_checker: public function { template FMT_CONSTEXPR typename std::enable_if< !is_integer::value, unsigned long long>::type operator()(T) { - return handler_.on_error("precision is not integer"), 0; + handler_.on_error("precision is not integer"); + return 0; } private: @@ -2056,8 +2075,10 @@ FMT_CONSTEXPR Iterator parse_format_specs(Iterator it, SpecHandler &&handler) { } if (align != ALIGN_DEFAULT) { if (p != it) { - if (c == '{') - return handler.on_error("invalid fill character '{'"), it; + if (c == '{') { + handler.on_error("invalid fill character '{'"); + return it; + } it += 2; handler.on_fill(c); } else ++it; From 94b476283caa0a5177653db7bc2d74719f66783c Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 4 Jun 2018 19:56:22 +0200 Subject: [PATCH 16/17] Fix docs --- doc/build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/build.py b/doc/build.py index ef45ebc4..5f45b2aa 100755 --- a/doc/build.py +++ b/doc/build.py @@ -56,7 +56,7 @@ def create_build_env(dirname='virtualenv'): pip_install('sphinx-doc/sphinx', '12b83372ac9316e8cbe86e7fed889296a4cc29ee', min_version='1.4.1.dev20160531') pip_install('michaeljones/breathe', - '6b1c5bb7a1866f15fc328b8716258354b10c1daa', + '129222318f7c8f865d2631e7da7b033567e7f56a', min_version='4.2.0') def build_docs(version='dev', **kwargs): From 911a75114be6023538991f7f73bae1cdc9879ada Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Mon, 4 Jun 2018 21:00:28 +0200 Subject: [PATCH 17/17] Fix nvcc warnings (#752) --- include/fmt/core.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/include/fmt/core.h b/include/fmt/core.h index 96bbf27c..094d1b6f 100644 --- a/include/fmt/core.h +++ b/include/fmt/core.h @@ -787,8 +787,9 @@ class arg_map { ~arg_map() { delete [] map_; } basic_format_arg find(basic_string_view name) const { + auto end = map_ + size_; // Define end outside of loop to make nvcc happy. // The list is unsorted, so just return the first matching name. - for (auto it = map_, end = map_ + size_; it != end; ++it) { + for (auto it = map_; it != end; ++it) { if (it->name == name) return it->arg; } @@ -973,16 +974,17 @@ class format_arg_store { friend class basic_format_args; - static FMT_CONSTEXPR uint64_t get_types() { - return IS_PACKED ? internal::get_types() - : -static_cast(NUM_ARGS); + static FMT_CONSTEXPR int64_t get_types() { + return IS_PACKED ? + static_cast(internal::get_types()) : + -static_cast(NUM_ARGS); } public: #if FMT_USE_CONSTEXPR - static constexpr uint64_t TYPES = get_types(); + static constexpr int64_t TYPES = get_types(); #else - static const uint64_t TYPES; + static const int64_t TYPES; #endif #if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405 @@ -998,7 +1000,7 @@ class format_arg_store { #if !FMT_USE_CONSTEXPR template -const uint64_t format_arg_store::TYPES = get_types(); +const int64_t format_arg_store::TYPES = get_types(); #endif /**