diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index d4ee7526..f0b94e0d 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -249,10 +249,11 @@ template inline int to_int(Int value) { template OutputIt static format_chrono_duration_value(OutputIt out, Rep val, int precision) { - if (precision < 0) - return format_to(out, "{}", val); - else - return format_to(out, "{:.{}f}", val, precision); + if (precision < 0) { + return format_to(out, std::is_floating_point::value ? "{:g}" : "{}", + val); + } + return format_to(out, "{:.{}f}", val, precision); } template diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index c1ec62f7..bb4b8423 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -470,8 +470,10 @@ FMT_FUNC bool grisu2_round(char* buf, int& size, int max_digits, uint64_t delta, FMT_FUNC int grisu2_gen_digits(char* buf, uint32_t hi, uint64_t lo, int& exp, uint64_t delta, const fp& one, const fp& diff, int max_digits) { + assert(exp <= 10); int size = 0; - // Generate digits for the most significant part (hi). + // Generate digits for the most significant part (hi). This can produce up to + // 10 digits. while (exp > 0) { uint32_t digit = 0; // This optimization by miloyip reduces the number of integer divisions by @@ -525,9 +527,7 @@ FMT_FUNC int grisu2_gen_digits(char* buf, uint32_t hi, uint64_t lo, int& exp, uint64_t remainder = (static_cast(hi) << -one.e) + lo; if (remainder <= delta || size > max_digits) { return grisu2_round(buf, size, max_digits, delta, remainder, - static_cast(data::POWERS_OF_10_64[exp]) - << -one.e, - diff.f, exp) + data::POWERS_OF_10_64[exp] << -one.e, diff.f, exp) ? size : -1; } @@ -572,7 +572,7 @@ struct prettify_handler { explicit prettify_handler(buffer& b, ptrdiff_t n) : data(b.data()), size(n), buf(b) {} ~prettify_handler() { - assert(buf.size() >= to_unsigned(size)); + assert(size <= inline_buffer_size); buf.resize(to_unsigned(size)); } @@ -617,7 +617,7 @@ template FMT_FUNC void write_exponent(int exp, Handler&& h) { h.append(d[1]); } else { const char* d = data::DIGITS + exp * 2; - h.append(d[0]); + if (d[0] != '0') h.append(d[0]); h.append(d[1]); } } @@ -633,25 +633,27 @@ struct fill { // The number is given as v = f * pow(10, exp), where f has size digits. template -FMT_FUNC void grisu2_prettify(const gen_digits_params& params, int size, - int exp, Handler&& handler) { +FMT_FUNC void grisu2_prettify(int size, int exp, Handler&& handler) { + // pow(10, full_exp - 1) <= v <= pow(10, full_exp). + int full_exp = size + exp; + auto params = gen_digits_params(); + params.fixed = (full_exp - 1) >= -4 && (full_exp - 1) <= 10; if (!params.fixed) { // Insert a decimal point after the first digit and add an exponent. - handler.insert(1, '.'); + if (size > 1) handler.insert(1, '.'); exp += size - 1; if (size < params.num_digits) handler.append(params.num_digits - size, '0'); handler.append(params.upper ? 'E' : 'e'); write_exponent(exp, handler); return; } - // pow(10, full_exp - 1) <= v <= pow(10, full_exp). - int full_exp = size + exp; + params.trailing_zeros = true; const int exp_threshold = 21; if (size <= full_exp && full_exp <= exp_threshold) { // 1234e7 -> 12340000000[.0+] handler.append(full_exp - size, '0'); - int num_zeros = params.num_digits - full_exp; - if (num_zeros > 0 && params.trailing_zeros) { + int num_zeros = std::max(params.num_digits - full_exp, 1); + if (params.trailing_zeros) { handler.append('.'); handler.append(num_zeros, '0'); } @@ -672,70 +674,14 @@ FMT_FUNC void grisu2_prettify(const gen_digits_params& params, int size, } } -struct char_counter { - ptrdiff_t size; - - template void insert(ptrdiff_t, ptrdiff_t n, F) { size += n; } - void insert(ptrdiff_t, char) { ++size; } - void append(ptrdiff_t n, char) { size += n; } - void append(char) { ++size; } - void remove_trailing(char) {} -}; - -// Converts format specifiers into parameters for digit generation and computes -// output buffer size for a number in the range [pow(10, exp - 1), pow(10, exp) -// or 0 if exp == 1. -FMT_FUNC gen_digits_params process_specs(const core_format_specs& specs, - int exp, buffer& buf) { - auto params = gen_digits_params(); - int num_digits = specs.precision >= 0 ? specs.precision : 6; - switch (specs.type) { - case 'G': - params.upper = true; - FMT_FALLTHROUGH - case '\0': - num_digits = 17; - case 'g': - params.trailing_zeros = (specs.flags & HASH_FLAG) != 0; - if (-4 <= exp && exp < num_digits + 1) { - params.fixed = true; - if (!specs.type && params.trailing_zeros && exp >= 0) - num_digits = exp + 1; - } - break; - case 'F': - params.upper = true; - FMT_FALLTHROUGH - case 'f': { - params.fixed = true; - params.trailing_zeros = true; - int adjusted_min_digits = num_digits + exp; - if (adjusted_min_digits > 0) num_digits = adjusted_min_digits; - break; - } - case 'E': - params.upper = true; - FMT_FALLTHROUGH - case 'e': - ++num_digits; - break; - } - params.num_digits = num_digits; - char_counter counter{num_digits}; - grisu2_prettify(params, params.num_digits, exp - num_digits, counter); - buf.resize(to_unsigned(counter.size)); - return params; -} - template FMT_FUNC typename std::enable_if::type -grisu2_format(Double value, buffer& buf, core_format_specs specs) { +grisu2_format(Double value, buffer& buf, core_format_specs) { FMT_ASSERT(value >= 0, "value is negative"); if (value <= 0) { // <= instead of == to silence a warning. - gen_digits_params params = process_specs(specs, 1, buf); - const size_t size = 1; buf[0] = '0'; - grisu2_prettify(params, size, 0, prettify_handler(buf, size)); + const int size = 1; + grisu2_prettify(size, 0, prettify_handler(buf, size)); return true; } @@ -752,6 +698,7 @@ grisu2_format(Double value, buffer& buf, core_format_specs specs) { upper = upper * cached_pow; // \tilde{M}^+ in Grisu. --upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}. fp one(1ull << -upper.e, upper.e); + assert(-60 <= upper.e && upper.e <= -32); // hi (p1 in Grisu) contains the most significant digits of scaled upper. // hi = floor(upper / one). uint32_t hi = static_cast(upper.f >> -one.e); @@ -765,14 +712,11 @@ grisu2_format(Double value, buffer& buf, core_format_specs specs) { // lo (p2 in Grisu) contains the least significants digits of scaled upper. // lo = upper % one. uint64_t lo = upper.f & (one.f - 1); - gen_digits_params params = process_specs(specs, cached_exp + exp, buf); - int size = grisu2_gen_digits(buf.data(), hi, lo, exp, delta, one, diff, - params.num_digits); - if (size < 0) { - buf.clear(); - return false; - } - grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size)); + const int max_digits = 20; + int size = + grisu2_gen_digits(buf.data(), hi, lo, exp, delta, one, diff, max_digits); + if (size < 0) return false; + grisu2_prettify(size, cached_exp + exp, prettify_handler(buf, size)); return true; } @@ -787,13 +731,20 @@ void sprintf_format(Double value, internal::buffer& buf, char format[MAX_FORMAT_SIZE]; char* format_ptr = format; *format_ptr++ = '%'; - if (spec.has(HASH_FLAG)) *format_ptr++ = '#'; + if (spec.has(HASH_FLAG) || !spec.type) *format_ptr++ = '#'; if (spec.precision >= 0) { *format_ptr++ = '.'; *format_ptr++ = '*'; } if (std::is_same::value) *format_ptr++ = 'L'; - *format_ptr++ = spec.type; + char type = spec.type ? spec.type : 'g'; +#if FMT_MSC_VER + if (type == 'F') { + // MSVC's printf doesn't support 'F'. + type = 'f'; + } +#endif + *format_ptr++ = type; *format_ptr = '\0'; // Format using snprintf. @@ -806,6 +757,22 @@ void sprintf_format(Double value, internal::buffer& buf, if (result >= 0) { unsigned n = internal::to_unsigned(result); if (n < buf.capacity()) { + if (!spec.type) { + // Keep only one trailing zero after the decimal point. + auto p = static_cast(std::memchr(buf.data(), '.', n)); + if (p) { + ++p; + if (*p == '0') ++p; + const char* end = buf.data() + n; + while (p != end && *p >= '1' && *p <= '9') ++p; + char* start = p; + while (p != end && *p == '0') ++p; + if (p == end || *p < '0' || *p > '9') { + if (p != end) std::memmove(start, p, to_unsigned(end - p)); + n -= static_cast(p - start); + } + } + } buf.resize(n); break; // The buffer is large enough - continue with formatting. } diff --git a/include/fmt/format.h b/include/fmt/format.h index afc4fbd2..11c25eba 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -172,11 +172,6 @@ FMT_END_NAMESPACE # define FMT_USE_TRAILING_RETURN 0 #endif -#ifndef FMT_USE_GRISU -# define FMT_USE_GRISU 0 -//# define FMT_USE_GRISU std::numeric_limits::is_iec559 -#endif - // __builtin_clz is broken in clang with Microsoft CodeGen: // https://github.com/fmtlib/fmt/issues/519 #ifndef _MSC_VER @@ -245,6 +240,15 @@ FMT_END_NAMESPACE FMT_BEGIN_NAMESPACE namespace internal { +#ifndef FMT_USE_GRISU +# define FMT_USE_GRISU 1 +#endif + +template inline bool use_grisu() { + return FMT_USE_GRISU && std::numeric_limits::is_iec559 && + sizeof(T) <= sizeof(double); +} + // An equivalent of `*reinterpret_cast(&source)` that doesn't produce // undefined behavior (e.g. due to type aliasing). // Example: uint64_t d = bit_cast(2.718); @@ -2675,10 +2679,7 @@ struct float_spec_handler { explicit float_spec_handler(char t) : type(t), upper(false) {} void on_general() { - if (type == 'G') - upper = true; - else - type = 'g'; + if (type == 'G') upper = true; } void on_exp() { @@ -2686,13 +2687,7 @@ struct float_spec_handler { } void on_fixed() { - if (type == 'F') { - upper = true; -#if FMT_MSC_VER - // MSVC's printf doesn't support 'F'. - type = 'f'; -#endif - } + if (type == 'F') upper = true; } void on_hex() { @@ -2737,14 +2732,9 @@ void basic_writer::write_double(T value, const format_specs& spec) { memory_buffer buffer; bool use_grisu = - FMT_USE_GRISU && sizeof(T) <= sizeof(double) && spec.type != 'a' && - spec.type != 'A' && + fmt::internal::use_grisu() && !spec.type && !spec.has_precision() && internal::grisu2_format(static_cast(value), buffer, spec); - if (!use_grisu) { - format_specs normalized_spec(spec); - normalized_spec.type = handler.type; - internal::sprintf_format(value, buffer, normalized_spec); - } + if (!use_grisu) internal::sprintf_format(value, buffer, spec); size_t n = buffer.size(); align_spec as = spec; if (spec.align() == ALIGN_NUMERIC) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0f349122..283e83b4 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -88,6 +88,7 @@ add_fmt_test(assert-test) add_fmt_test(chrono-test) add_fmt_test(core-test) add_fmt_test(grisu-test) +target_compile_definitions(grisu-test PRIVATE FMT_USE_GRISU=1) add_fmt_test(gtest-extra-test) add_fmt_test(format-test mock-allocator.h) if (NOT (MSVC AND BUILD_SHARED_LIBS)) diff --git a/test/chrono-test.cc b/test/chrono-test.cc index 363fafaa..4ef545af 100644 --- a/test/chrono-test.cc +++ b/test/chrono-test.cc @@ -230,8 +230,8 @@ TEST(ChronoTest, FormatSimpleQq) { TEST(ChronoTest, FormatPrecisionQq) { EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)), - fmt::format_error, - "precision not allowed for this argument type"); + fmt::format_error, + "precision not allowed for this argument type"); EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2)); } diff --git a/test/format-test.cc b/test/format-test.cc index 500f15ca..5db36526 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -635,8 +635,15 @@ TEST(WriterTest, WriteLongLong) { TEST(WriterTest, WriteDouble) { CHECK_WRITE(4.2); CHECK_WRITE(-4.2); - CHECK_WRITE(std::numeric_limits::min()); - CHECK_WRITE(std::numeric_limits::max()); + auto min = std::numeric_limits::min(); + auto max = std::numeric_limits::max(); + if (fmt::internal::use_grisu()) { + EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min)); + EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max)); + } else { + CHECK_WRITE(min); + CHECK_WRITE(max); + } } TEST(WriterTest, WriteLongDouble) { @@ -648,8 +655,15 @@ TEST(WriterTest, WriteLongDouble) { CHECK_WRITE_WCHAR(-4.2l); else fmt::print("warning: long double formatting with std::swprintf is broken"); - CHECK_WRITE(std::numeric_limits::min()); - CHECK_WRITE(std::numeric_limits::max()); + auto min = std::numeric_limits::min(); + auto max = std::numeric_limits::max(); + if (fmt::internal::use_grisu()) { + EXPECT_EQ("2.2250738585072014e-308", fmt::format("{}", min)); + EXPECT_EQ("1.7976931348623157e+308", fmt::format("{}", max)); + } else { + CHECK_WRITE(min); + CHECK_WRITE(max); + } } TEST(WriterTest, WriteDoubleAtBufferBoundary) { @@ -709,7 +723,7 @@ TEST(FormatToTest, WideString) { TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) { char buffer[16] = {}; fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0); - EXPECT_STREQ("+42", buffer); + EXPECT_STREQ("+42.0", buffer); } TEST(FormatToTest, FormatToMemoryBuffer) { @@ -841,8 +855,8 @@ TEST(FormatterTest, LeftAlign) { EXPECT_EQ("42 ", format("{0:<5}", 42ul)); EXPECT_EQ("-42 ", format("{0:<5}", -42ll)); EXPECT_EQ("42 ", format("{0:<5}", 42ull)); - EXPECT_EQ("-42 ", format("{0:<5}", -42.0)); - EXPECT_EQ("-42 ", format("{0:<5}", -42.0l)); + EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0)); + EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0l)); EXPECT_EQ("c ", format("{0:<5}", 'c')); EXPECT_EQ("abc ", format("{0:<5}", "abc")); EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast(0xface))); @@ -858,8 +872,8 @@ TEST(FormatterTest, RightAlign) { EXPECT_EQ(" 42", format("{0:>5}", 42ul)); EXPECT_EQ(" -42", format("{0:>5}", -42ll)); EXPECT_EQ(" 42", format("{0:>5}", 42ull)); - EXPECT_EQ(" -42", format("{0:>5}", -42.0)); - EXPECT_EQ(" -42", format("{0:>5}", -42.0l)); + EXPECT_EQ(" -42.0", format("{0:>7}", -42.0)); + EXPECT_EQ(" -42.0", format("{0:>7}", -42.0l)); EXPECT_EQ(" c", format("{0:>5}", 'c')); EXPECT_EQ(" abc", format("{0:>5}", "abc")); EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast(0xface))); @@ -878,8 +892,8 @@ TEST(FormatterTest, NumericAlign) { EXPECT_EQ(" 42", format("{0:=5}", 42ul)); EXPECT_EQ("- 42", format("{0:=5}", -42ll)); EXPECT_EQ(" 42", format("{0:=5}", 42ull)); - EXPECT_EQ("- 42", format("{0:=5}", -42.0)); - EXPECT_EQ("- 42", format("{0:=5}", -42.0l)); + EXPECT_EQ("- 42.0", format("{0:=7}", -42.0)); + EXPECT_EQ("- 42.0", format("{0:=7}", -42.0l)); EXPECT_THROW_MSG(format("{0:=5", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0:=5}", 'c'), format_error, @@ -888,7 +902,7 @@ TEST(FormatterTest, NumericAlign) { "format specifier requires numeric argument"); EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast(0xface)), format_error, "format specifier requires numeric argument"); - EXPECT_EQ(" 1", fmt::format("{:= }", 1.0)); + EXPECT_EQ(" 1.0", fmt::format("{:= }", 1.0)); } TEST(FormatterTest, CenterAlign) { @@ -901,8 +915,8 @@ TEST(FormatterTest, CenterAlign) { EXPECT_EQ(" 42 ", format("{0:^5}", 42ul)); EXPECT_EQ(" -42 ", format("{0:^5}", -42ll)); EXPECT_EQ(" 42 ", format("{0:^5}", 42ull)); - EXPECT_EQ(" -42 ", format("{0:^6}", -42.0)); - EXPECT_EQ(" -42 ", format("{0:^5}", -42.0l)); + EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0)); + EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0l)); EXPECT_EQ(" c ", format("{0:^5}", 'c')); EXPECT_EQ(" abc ", format("{0:^6}", "abc")); EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast(0xface))); @@ -920,8 +934,8 @@ TEST(FormatterTest, Fill) { EXPECT_EQ("***42", format("{0:*>5}", 42ul)); EXPECT_EQ("**-42", format("{0:*>5}", -42ll)); EXPECT_EQ("***42", format("{0:*>5}", 42ull)); - EXPECT_EQ("**-42", format("{0:*>5}", -42.0)); - EXPECT_EQ("**-42", format("{0:*>5}", -42.0l)); + EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0)); + EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0l)); EXPECT_EQ("c****", format("{0:*<5}", 'c')); EXPECT_EQ("abc**", format("{0:*<5}", "abc")); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast(0xface))); @@ -941,8 +955,8 @@ TEST(FormatterTest, PlusSign) { EXPECT_EQ("+42", format("{0:+}", 42ll)); EXPECT_THROW_MSG(format("{0:+}", 42ull), format_error, "format specifier requires signed argument"); - EXPECT_EQ("+42", format("{0:+}", 42.0)); - EXPECT_EQ("+42", format("{0:+}", 42.0l)); + EXPECT_EQ("+42.0", format("{0:+}", 42.0)); + EXPECT_EQ("+42.0", format("{0:+}", 42.0l)); EXPECT_THROW_MSG(format("{0:+", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0:+}", 'c'), format_error, @@ -965,8 +979,8 @@ TEST(FormatterTest, MinusSign) { EXPECT_EQ("42", format("{0:-}", 42ll)); EXPECT_THROW_MSG(format("{0:-}", 42ull), format_error, "format specifier requires signed argument"); - EXPECT_EQ("42", format("{0:-}", 42.0)); - EXPECT_EQ("42", format("{0:-}", 42.0l)); + EXPECT_EQ("42.0", format("{0:-}", 42.0)); + EXPECT_EQ("42.0", format("{0:-}", 42.0l)); EXPECT_THROW_MSG(format("{0:-", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0:-}", 'c'), format_error, @@ -989,8 +1003,8 @@ TEST(FormatterTest, SpaceSign) { EXPECT_EQ(" 42", format("{0: }", 42ll)); EXPECT_THROW_MSG(format("{0: }", 42ull), format_error, "format specifier requires signed argument"); - EXPECT_EQ(" 42", format("{0: }", 42.0)); - EXPECT_EQ(" 42", format("{0: }", 42.0l)); + EXPECT_EQ(" 42.0", format("{0: }", 42.0)); + EXPECT_EQ(" 42.0", format("{0: }", 42.0l)); EXPECT_THROW_MSG(format("{0: ", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0: }", 'c'), format_error, @@ -1034,11 +1048,8 @@ TEST(FormatterTest, HashFlag) { EXPECT_EQ("0x42", format("{0:#x}", 0x42ull)); EXPECT_EQ("042", format("{0:#o}", 042ull)); - if (FMT_USE_GRISU) - EXPECT_EQ("-42.0", format("{0:#}", -42.0)); - else - EXPECT_EQ("-42.0000", format("{0:#}", -42.0)); - EXPECT_EQ("-42.0000", format("{0:#}", -42.0l)); + EXPECT_EQ("-42.0", format("{0:#}", -42.0)); + EXPECT_EQ("-42.0", format("{0:#}", -42.0l)); EXPECT_THROW_MSG(format("{0:#", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error, @@ -1057,8 +1068,8 @@ TEST(FormatterTest, ZeroFlag) { EXPECT_EQ("00042", format("{0:05}", 42ul)); EXPECT_EQ("-0042", format("{0:05}", -42ll)); EXPECT_EQ("00042", format("{0:05}", 42ull)); - EXPECT_EQ("-0042", format("{0:05}", -42.0)); - EXPECT_EQ("-0042", format("{0:05}", -42.0l)); + EXPECT_EQ("-0042.0", format("{0:07}", -42.0)); + EXPECT_EQ("-0042.0", format("{0:07}", -42.0l)); EXPECT_THROW_MSG(format("{0:0", 'c'), format_error, "missing '}' in format string"); EXPECT_THROW_MSG(format("{0:05}", 'c'), format_error, @@ -1433,7 +1444,7 @@ TEST(FormatterTest, FormatFloat) { TEST(FormatterTest, FormatDouble) { check_unknown_types(1.2, "eEfFgGaA", "double"); - EXPECT_EQ("0", format("{:}", 0.0)); + EXPECT_EQ("0.0", format("{:}", 0.0)); EXPECT_EQ("0.000000", format("{:f}", 0.0)); EXPECT_EQ("0", format("{:g}", 0.0)); EXPECT_EQ("392.65", format("{:}", 392.65)); @@ -1453,12 +1464,6 @@ TEST(FormatterTest, FormatDouble) { EXPECT_EQ(buffer, format("{:A}", -42.0)); } -TEST(FormatterTest, FormatDoubleBigPrecision) { - // sprintf with big precision is broken in MSVC2013, so only test on Grisu. - if (FMT_USE_GRISU) - EXPECT_EQ(format("0.{:0<1000}", ""), format("{:.1000f}", 0.0)); -} - TEST(FormatterTest, FormatNaN) { double nan = std::numeric_limits::quiet_NaN(); EXPECT_EQ("nan", format("{}", nan)); @@ -1483,7 +1488,7 @@ TEST(FormatterTest, FormatInfinity) { } TEST(FormatterTest, FormatLongDouble) { - EXPECT_EQ("0", format("{0:}", 0.0l)); + EXPECT_EQ("0.0", format("{0:}", 0.0l)); EXPECT_EQ("0.000000", format("{0:f}", 0.0l)); EXPECT_EQ("392.65", format("{0:}", 392.65l)); EXPECT_EQ("392.65", format("{0:g}", 392.65l)); @@ -1721,29 +1726,29 @@ TEST(FormatIntTest, FormatInt) { } #if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif #if FMT_MSC_VER -#pragma warning(push) -#pragma warning(disable: 4996) // Using a deprecated function +# pragma warning(push) +# pragma warning(disable : 4996) // Using a deprecated function #endif template std::string format_decimal(T value) { char buffer[10]; char* ptr = buffer; // TODO: Replace with safer, non-deprecated overload - fmt::format_decimal(ptr, value); + fmt::format_decimal(ptr, value); return std::string(buffer, ptr); } #if FMT_MSC_VER -#pragma warning(pop) +# pragma warning(pop) #endif #if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop +# pragma GCC diagnostic pop #endif TEST(FormatIntTest, FormatDec) { @@ -1781,13 +1786,13 @@ TEST(FormatTest, Dynamic) { std::vector> args; args.emplace_back(fmt::internal::make_arg(42)); args.emplace_back(fmt::internal::make_arg("abc1")); - args.emplace_back(fmt::internal::make_arg(1.2f)); + args.emplace_back(fmt::internal::make_arg(1.5f)); std::string result = fmt::vformat( "{} and {} and {}", fmt::basic_format_args( args.data(), static_cast(args.size()))); - EXPECT_EQ("42 and abc1 and 1.2", result); + EXPECT_EQ("42 and abc1 and 1.5", result); } TEST(FormatTest, JoinArg) { @@ -2376,8 +2381,8 @@ TEST(FormatTest, FormatStringErrors) { EXPECT_ERROR("{:d}", "invalid type specifier", std::string); EXPECT_ERROR("{:s}", "invalid type specifier", void*); # endif - EXPECT_ERROR("{foo", - "compile-time checks don't support named arguments", int); + EXPECT_ERROR("{foo", "compile-time checks don't support named arguments", + int); EXPECT_ERROR_NOARGS("{10000000000}", "number is too big"); EXPECT_ERROR_NOARGS("{0x}", "invalid format string"); EXPECT_ERROR_NOARGS("{-}", "invalid format string"); diff --git a/test/grisu-test.cc b/test/grisu-test.cc index 49242766..7d7912c9 100644 --- a/test/grisu-test.cc +++ b/test/grisu-test.cc @@ -5,7 +5,6 @@ // // For the license information refer to format.h. -#define FMT_USE_GRISU std::numeric_limits::is_iec559 #include "fmt/format.h" #include "gtest.h" @@ -36,11 +35,21 @@ TEST(GrisuTest, Inf) { EXPECT_EQ("-inf", fmt::format("{}", -inf)); } -TEST(GrisuTest, Zero) { - EXPECT_EQ("0", fmt::format("{}", 0.0)); -} +TEST(GrisuTest, Zero) { EXPECT_EQ("0.0", fmt::format("{}", 0.0)); } TEST(GrisuTest, Round) { EXPECT_EQ("1.9156918820264798e-56", fmt::format("{}", 1.9156918820264798e-56)); } + +TEST(GrisuTest, Prettify) { + EXPECT_EQ("0.0001", fmt::format("{}", 1e-4)); + EXPECT_EQ("1e-5", fmt::format("{}", 1e-5)); + EXPECT_EQ("9.999e-5", fmt::format("{}", 9.999e-5)); + EXPECT_EQ("10000000000.0", fmt::format("{}", 1e10)); + EXPECT_EQ("1e+11", fmt::format("{}", 1e11)); + EXPECT_EQ("12340000000.0", fmt::format("{}", 1234e7)); + EXPECT_EQ("12.34", fmt::format("{}", 1234e-2)); + EXPECT_EQ("0.001234", fmt::format("{}", 1234e-6)); + +} diff --git a/test/printf-test.cc b/test/printf-test.cc index 8cfe5205..eda15864 100644 --- a/test/printf-test.cc +++ b/test/printf-test.cc @@ -368,8 +368,8 @@ TEST(PrintfTest, Length) { TestLength("z"); TestLength("t"); long double max = std::numeric_limits::max(); - EXPECT_PRINTF(fmt::format("{}", max), "%g", max); - EXPECT_PRINTF(fmt::format("{}", max), "%Lg", max); + EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max); + EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max); } TEST(PrintfTest, Bool) {