Enable grisu for shortest roundtrip (default) formatting

This commit is contained in:
Victor Zverovich
2019-02-03 07:44:42 -08:00
parent b8d34e0db3
commit 355eb6d29a
8 changed files with 138 additions and 165 deletions

View File

@ -249,10 +249,11 @@ template <typename Int> inline int to_int(Int value) {
template <typename Rep, typename OutputIt> template <typename Rep, typename OutputIt>
OutputIt static format_chrono_duration_value(OutputIt out, Rep val, OutputIt static format_chrono_duration_value(OutputIt out, Rep val,
int precision) { int precision) {
if (precision < 0) if (precision < 0) {
return format_to(out, "{}", val); return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
else val);
return format_to(out, "{:.{}f}", val, precision); }
return format_to(out, "{:.{}f}", val, precision);
} }
template <typename Period, typename OutputIt> template <typename Period, typename OutputIt>

View File

@ -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, 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, uint64_t delta, const fp& one, const fp& diff,
int max_digits) { int max_digits) {
assert(exp <= 10);
int size = 0; 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) { while (exp > 0) {
uint32_t digit = 0; uint32_t digit = 0;
// This optimization by miloyip reduces the number of integer divisions by // 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<uint64_t>(hi) << -one.e) + lo; uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
if (remainder <= delta || size > max_digits) { if (remainder <= delta || size > max_digits) {
return grisu2_round(buf, size, max_digits, delta, remainder, return grisu2_round(buf, size, max_digits, delta, remainder,
static_cast<uint64_t>(data::POWERS_OF_10_64[exp]) data::POWERS_OF_10_64[exp] << -one.e, diff.f, exp)
<< -one.e,
diff.f, exp)
? size ? size
: -1; : -1;
} }
@ -572,7 +572,7 @@ struct prettify_handler {
explicit prettify_handler(buffer& b, ptrdiff_t n) explicit prettify_handler(buffer& b, ptrdiff_t n)
: data(b.data()), size(n), buf(b) {} : data(b.data()), size(n), buf(b) {}
~prettify_handler() { ~prettify_handler() {
assert(buf.size() >= to_unsigned(size)); assert(size <= inline_buffer_size);
buf.resize(to_unsigned(size)); buf.resize(to_unsigned(size));
} }
@ -617,7 +617,7 @@ template <typename Handler> FMT_FUNC void write_exponent(int exp, Handler&& h) {
h.append(d[1]); h.append(d[1]);
} else { } else {
const char* d = data::DIGITS + exp * 2; const char* d = data::DIGITS + exp * 2;
h.append(d[0]); if (d[0] != '0') h.append(d[0]);
h.append(d[1]); 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. // The number is given as v = f * pow(10, exp), where f has size digits.
template <typename Handler> template <typename Handler>
FMT_FUNC void grisu2_prettify(const gen_digits_params& params, int size, FMT_FUNC void grisu2_prettify(int size, int exp, Handler&& handler) {
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) { if (!params.fixed) {
// Insert a decimal point after the first digit and add an exponent. // Insert a decimal point after the first digit and add an exponent.
handler.insert(1, '.'); if (size > 1) handler.insert(1, '.');
exp += size - 1; exp += size - 1;
if (size < params.num_digits) handler.append(params.num_digits - size, '0'); if (size < params.num_digits) handler.append(params.num_digits - size, '0');
handler.append(params.upper ? 'E' : 'e'); handler.append(params.upper ? 'E' : 'e');
write_exponent(exp, handler); write_exponent(exp, handler);
return; return;
} }
// pow(10, full_exp - 1) <= v <= pow(10, full_exp). params.trailing_zeros = true;
int full_exp = size + exp;
const int exp_threshold = 21; const int exp_threshold = 21;
if (size <= full_exp && full_exp <= exp_threshold) { if (size <= full_exp && full_exp <= exp_threshold) {
// 1234e7 -> 12340000000[.0+] // 1234e7 -> 12340000000[.0+]
handler.append(full_exp - size, '0'); handler.append(full_exp - size, '0');
int num_zeros = params.num_digits - full_exp; int num_zeros = std::max(params.num_digits - full_exp, 1);
if (num_zeros > 0 && params.trailing_zeros) { if (params.trailing_zeros) {
handler.append('.'); handler.append('.');
handler.append(num_zeros, '0'); 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 <typename F> 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 <typename Double> template <typename Double>
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::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"); FMT_ASSERT(value >= 0, "value is negative");
if (value <= 0) { // <= instead of == to silence a warning. 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'; 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; 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 = upper * cached_pow; // \tilde{M}^+ in Grisu.
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}. --upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
fp one(1ull << -upper.e, upper.e); 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 (p1 in Grisu) contains the most significant digits of scaled upper.
// hi = floor(upper / one). // hi = floor(upper / one).
uint32_t hi = static_cast<uint32_t>(upper.f >> -one.e); uint32_t hi = static_cast<uint32_t>(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 (p2 in Grisu) contains the least significants digits of scaled upper.
// lo = upper % one. // lo = upper % one.
uint64_t lo = upper.f & (one.f - 1); uint64_t lo = upper.f & (one.f - 1);
gen_digits_params params = process_specs(specs, cached_exp + exp, buf); const int max_digits = 20;
int size = grisu2_gen_digits(buf.data(), hi, lo, exp, delta, one, diff, int size =
params.num_digits); grisu2_gen_digits(buf.data(), hi, lo, exp, delta, one, diff, max_digits);
if (size < 0) { if (size < 0) return false;
buf.clear(); grisu2_prettify(size, cached_exp + exp, prettify_handler(buf, size));
return false;
}
grisu2_prettify(params, size, cached_exp + exp, prettify_handler(buf, size));
return true; return true;
} }
@ -787,13 +731,20 @@ void sprintf_format(Double value, internal::buffer& buf,
char format[MAX_FORMAT_SIZE]; char format[MAX_FORMAT_SIZE];
char* format_ptr = format; char* format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
if (spec.has(HASH_FLAG)) *format_ptr++ = '#'; if (spec.has(HASH_FLAG) || !spec.type) *format_ptr++ = '#';
if (spec.precision >= 0) { if (spec.precision >= 0) {
*format_ptr++ = '.'; *format_ptr++ = '.';
*format_ptr++ = '*'; *format_ptr++ = '*';
} }
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L'; if (std::is_same<Double, long double>::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_ptr = '\0';
// Format using snprintf. // Format using snprintf.
@ -806,6 +757,22 @@ void sprintf_format(Double value, internal::buffer& buf,
if (result >= 0) { if (result >= 0) {
unsigned n = internal::to_unsigned(result); unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) { if (n < buf.capacity()) {
if (!spec.type) {
// Keep only one trailing zero after the decimal point.
auto p = static_cast<char*>(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<unsigned>(p - start);
}
}
}
buf.resize(n); buf.resize(n);
break; // The buffer is large enough - continue with formatting. break; // The buffer is large enough - continue with formatting.
} }

View File

@ -172,11 +172,6 @@ FMT_END_NAMESPACE
# define FMT_USE_TRAILING_RETURN 0 # define FMT_USE_TRAILING_RETURN 0
#endif #endif
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU 0
//# define FMT_USE_GRISU std::numeric_limits<double>::is_iec559
#endif
// __builtin_clz is broken in clang with Microsoft CodeGen: // __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519 // https://github.com/fmtlib/fmt/issues/519
#ifndef _MSC_VER #ifndef _MSC_VER
@ -245,6 +240,15 @@ FMT_END_NAMESPACE
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
#ifndef FMT_USE_GRISU
# define FMT_USE_GRISU 1
#endif
template <typename T> inline bool use_grisu() {
return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559 &&
sizeof(T) <= sizeof(double);
}
// An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce // An equivalent of `*reinterpret_cast<Dest*>(&source)` that doesn't produce
// undefined behavior (e.g. due to type aliasing). // undefined behavior (e.g. due to type aliasing).
// Example: uint64_t d = bit_cast<uint64_t>(2.718); // Example: uint64_t d = bit_cast<uint64_t>(2.718);
@ -2675,10 +2679,7 @@ struct float_spec_handler {
explicit float_spec_handler(char t) : type(t), upper(false) {} explicit float_spec_handler(char t) : type(t), upper(false) {}
void on_general() { void on_general() {
if (type == 'G') if (type == 'G') upper = true;
upper = true;
else
type = 'g';
} }
void on_exp() { void on_exp() {
@ -2686,13 +2687,7 @@ struct float_spec_handler {
} }
void on_fixed() { void on_fixed() {
if (type == 'F') { if (type == 'F') upper = true;
upper = true;
#if FMT_MSC_VER
// MSVC's printf doesn't support 'F'.
type = 'f';
#endif
}
} }
void on_hex() { void on_hex() {
@ -2737,14 +2732,9 @@ void basic_writer<Range>::write_double(T value, const format_specs& spec) {
memory_buffer buffer; memory_buffer buffer;
bool use_grisu = bool use_grisu =
FMT_USE_GRISU && sizeof(T) <= sizeof(double) && spec.type != 'a' && fmt::internal::use_grisu<T>() && !spec.type && !spec.has_precision() &&
spec.type != 'A' &&
internal::grisu2_format(static_cast<double>(value), buffer, spec); internal::grisu2_format(static_cast<double>(value), buffer, spec);
if (!use_grisu) { if (!use_grisu) internal::sprintf_format(value, buffer, spec);
format_specs normalized_spec(spec);
normalized_spec.type = handler.type;
internal::sprintf_format(value, buffer, normalized_spec);
}
size_t n = buffer.size(); size_t n = buffer.size();
align_spec as = spec; align_spec as = spec;
if (spec.align() == ALIGN_NUMERIC) { if (spec.align() == ALIGN_NUMERIC) {

View File

@ -88,6 +88,7 @@ add_fmt_test(assert-test)
add_fmt_test(chrono-test) add_fmt_test(chrono-test)
add_fmt_test(core-test) add_fmt_test(core-test)
add_fmt_test(grisu-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(gtest-extra-test)
add_fmt_test(format-test mock-allocator.h) add_fmt_test(format-test mock-allocator.h)
if (NOT (MSVC AND BUILD_SHARED_LIBS)) if (NOT (MSVC AND BUILD_SHARED_LIBS))

View File

@ -230,8 +230,8 @@ TEST(ChronoTest, FormatSimpleQq) {
TEST(ChronoTest, FormatPrecisionQq) { TEST(ChronoTest, FormatPrecisionQq) {
EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)), EXPECT_THROW_MSG(fmt::format("{:.2%Q %q}", std::chrono::seconds(42)),
fmt::format_error, fmt::format_error,
"precision not allowed for this argument type"); "precision not allowed for this argument type");
EXPECT_EQ("1.2 ms", fmt::format("{:.1%Q %q}", dms(1.234))); 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)); EXPECT_EQ("1.23 ms", fmt::format("{:.{}%Q %q}", dms(1.234), 2));
} }

View File

@ -635,8 +635,15 @@ TEST(WriterTest, WriteLongLong) {
TEST(WriterTest, WriteDouble) { TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2); CHECK_WRITE(4.2);
CHECK_WRITE(-4.2); CHECK_WRITE(-4.2);
CHECK_WRITE(std::numeric_limits<double>::min()); auto min = std::numeric_limits<double>::min();
CHECK_WRITE(std::numeric_limits<double>::max()); auto max = std::numeric_limits<double>::max();
if (fmt::internal::use_grisu<double>()) {
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) { TEST(WriterTest, WriteLongDouble) {
@ -648,8 +655,15 @@ TEST(WriterTest, WriteLongDouble) {
CHECK_WRITE_WCHAR(-4.2l); CHECK_WRITE_WCHAR(-4.2l);
else else
fmt::print("warning: long double formatting with std::swprintf is broken"); fmt::print("warning: long double formatting with std::swprintf is broken");
CHECK_WRITE(std::numeric_limits<long double>::min()); auto min = std::numeric_limits<long double>::min();
CHECK_WRITE(std::numeric_limits<long double>::max()); auto max = std::numeric_limits<long double>::max();
if (fmt::internal::use_grisu<long double>()) {
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) { TEST(WriterTest, WriteDoubleAtBufferBoundary) {
@ -709,7 +723,7 @@ TEST(FormatToTest, WideString) {
TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) { TEST(FormatToTest, FormatToNonbackInsertIteratorWithSignAndNumericAlignment) {
char buffer[16] = {}; char buffer[16] = {};
fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0); fmt::format_to(fmt::internal::make_checked(buffer, 16), "{: =+}", 42.0);
EXPECT_STREQ("+42", buffer); EXPECT_STREQ("+42.0", buffer);
} }
TEST(FormatToTest, FormatToMemoryBuffer) { TEST(FormatToTest, FormatToMemoryBuffer) {
@ -841,8 +855,8 @@ TEST(FormatterTest, LeftAlign) {
EXPECT_EQ("42 ", format("{0:<5}", 42ul)); EXPECT_EQ("42 ", format("{0:<5}", 42ul));
EXPECT_EQ("-42 ", format("{0:<5}", -42ll)); EXPECT_EQ("-42 ", format("{0:<5}", -42ll));
EXPECT_EQ("42 ", format("{0:<5}", 42ull)); EXPECT_EQ("42 ", format("{0:<5}", 42ull));
EXPECT_EQ("-42 ", format("{0:<5}", -42.0)); EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0));
EXPECT_EQ("-42 ", format("{0:<5}", -42.0l)); EXPECT_EQ("-42.0 ", format("{0:<7}", -42.0l));
EXPECT_EQ("c ", format("{0:<5}", 'c')); EXPECT_EQ("c ", format("{0:<5}", 'c'));
EXPECT_EQ("abc ", format("{0:<5}", "abc")); EXPECT_EQ("abc ", format("{0:<5}", "abc"));
EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ("0xface ", format("{0:<8}", reinterpret_cast<void*>(0xface)));
@ -858,8 +872,8 @@ TEST(FormatterTest, RightAlign) {
EXPECT_EQ(" 42", format("{0:>5}", 42ul)); EXPECT_EQ(" 42", format("{0:>5}", 42ul));
EXPECT_EQ(" -42", format("{0:>5}", -42ll)); EXPECT_EQ(" -42", format("{0:>5}", -42ll));
EXPECT_EQ(" 42", format("{0:>5}", 42ull)); EXPECT_EQ(" 42", format("{0:>5}", 42ull));
EXPECT_EQ(" -42", format("{0:>5}", -42.0)); EXPECT_EQ(" -42.0", format("{0:>7}", -42.0));
EXPECT_EQ(" -42", format("{0:>5}", -42.0l)); EXPECT_EQ(" -42.0", format("{0:>7}", -42.0l));
EXPECT_EQ(" c", format("{0:>5}", 'c')); EXPECT_EQ(" c", format("{0:>5}", 'c'));
EXPECT_EQ(" abc", format("{0:>5}", "abc")); EXPECT_EQ(" abc", format("{0:>5}", "abc"));
EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ(" 0xface", format("{0:>8}", reinterpret_cast<void*>(0xface)));
@ -878,8 +892,8 @@ TEST(FormatterTest, NumericAlign) {
EXPECT_EQ(" 42", format("{0:=5}", 42ul)); EXPECT_EQ(" 42", format("{0:=5}", 42ul));
EXPECT_EQ("- 42", format("{0:=5}", -42ll)); EXPECT_EQ("- 42", format("{0:=5}", -42ll));
EXPECT_EQ(" 42", format("{0:=5}", 42ull)); EXPECT_EQ(" 42", format("{0:=5}", 42ull));
EXPECT_EQ("- 42", format("{0:=5}", -42.0)); EXPECT_EQ("- 42.0", format("{0:=7}", -42.0));
EXPECT_EQ("- 42", format("{0:=5}", -42.0l)); EXPECT_EQ("- 42.0", format("{0:=7}", -42.0l));
EXPECT_THROW_MSG(format("{0:=5", 'c'), format_error, EXPECT_THROW_MSG(format("{0:=5", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0:=5}", 'c'), format_error, EXPECT_THROW_MSG(format("{0:=5}", 'c'), format_error,
@ -888,7 +902,7 @@ TEST(FormatterTest, NumericAlign) {
"format specifier requires numeric argument"); "format specifier requires numeric argument");
EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)), EXPECT_THROW_MSG(format("{0:=8}", reinterpret_cast<void*>(0xface)),
format_error, "format specifier requires numeric argument"); 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) { TEST(FormatterTest, CenterAlign) {
@ -901,8 +915,8 @@ TEST(FormatterTest, CenterAlign) {
EXPECT_EQ(" 42 ", format("{0:^5}", 42ul)); EXPECT_EQ(" 42 ", format("{0:^5}", 42ul));
EXPECT_EQ(" -42 ", format("{0:^5}", -42ll)); EXPECT_EQ(" -42 ", format("{0:^5}", -42ll));
EXPECT_EQ(" 42 ", format("{0:^5}", 42ull)); EXPECT_EQ(" 42 ", format("{0:^5}", 42ull));
EXPECT_EQ(" -42 ", format("{0:^6}", -42.0)); EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0));
EXPECT_EQ(" -42 ", format("{0:^5}", -42.0l)); EXPECT_EQ(" -42.0 ", format("{0:^7}", -42.0l));
EXPECT_EQ(" c ", format("{0:^5}", 'c')); EXPECT_EQ(" c ", format("{0:^5}", 'c'));
EXPECT_EQ(" abc ", format("{0:^6}", "abc")); EXPECT_EQ(" abc ", format("{0:^6}", "abc"));
EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ(" 0xface ", format("{0:^8}", reinterpret_cast<void*>(0xface)));
@ -920,8 +934,8 @@ TEST(FormatterTest, Fill) {
EXPECT_EQ("***42", format("{0:*>5}", 42ul)); EXPECT_EQ("***42", format("{0:*>5}", 42ul));
EXPECT_EQ("**-42", format("{0:*>5}", -42ll)); EXPECT_EQ("**-42", format("{0:*>5}", -42ll));
EXPECT_EQ("***42", format("{0:*>5}", 42ull)); EXPECT_EQ("***42", format("{0:*>5}", 42ull));
EXPECT_EQ("**-42", format("{0:*>5}", -42.0)); EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0));
EXPECT_EQ("**-42", format("{0:*>5}", -42.0l)); EXPECT_EQ("**-42.0", format("{0:*>7}", -42.0l));
EXPECT_EQ("c****", format("{0:*<5}", 'c')); EXPECT_EQ("c****", format("{0:*<5}", 'c'));
EXPECT_EQ("abc**", format("{0:*<5}", "abc")); EXPECT_EQ("abc**", format("{0:*<5}", "abc"));
EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface))); EXPECT_EQ("**0xface", format("{0:*>8}", reinterpret_cast<void*>(0xface)));
@ -941,8 +955,8 @@ TEST(FormatterTest, PlusSign) {
EXPECT_EQ("+42", format("{0:+}", 42ll)); EXPECT_EQ("+42", format("{0:+}", 42ll));
EXPECT_THROW_MSG(format("{0:+}", 42ull), format_error, EXPECT_THROW_MSG(format("{0:+}", 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("+42", format("{0:+}", 42.0)); EXPECT_EQ("+42.0", format("{0:+}", 42.0));
EXPECT_EQ("+42", format("{0:+}", 42.0l)); EXPECT_EQ("+42.0", format("{0:+}", 42.0l));
EXPECT_THROW_MSG(format("{0:+", 'c'), format_error, EXPECT_THROW_MSG(format("{0:+", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0:+}", 'c'), format_error, EXPECT_THROW_MSG(format("{0:+}", 'c'), format_error,
@ -965,8 +979,8 @@ TEST(FormatterTest, MinusSign) {
EXPECT_EQ("42", format("{0:-}", 42ll)); EXPECT_EQ("42", format("{0:-}", 42ll));
EXPECT_THROW_MSG(format("{0:-}", 42ull), format_error, EXPECT_THROW_MSG(format("{0:-}", 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ("42", format("{0:-}", 42.0)); EXPECT_EQ("42.0", format("{0:-}", 42.0));
EXPECT_EQ("42", format("{0:-}", 42.0l)); EXPECT_EQ("42.0", format("{0:-}", 42.0l));
EXPECT_THROW_MSG(format("{0:-", 'c'), format_error, EXPECT_THROW_MSG(format("{0:-", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0:-}", 'c'), format_error, EXPECT_THROW_MSG(format("{0:-}", 'c'), format_error,
@ -989,8 +1003,8 @@ TEST(FormatterTest, SpaceSign) {
EXPECT_EQ(" 42", format("{0: }", 42ll)); EXPECT_EQ(" 42", format("{0: }", 42ll));
EXPECT_THROW_MSG(format("{0: }", 42ull), format_error, EXPECT_THROW_MSG(format("{0: }", 42ull), format_error,
"format specifier requires signed argument"); "format specifier requires signed argument");
EXPECT_EQ(" 42", format("{0: }", 42.0)); EXPECT_EQ(" 42.0", format("{0: }", 42.0));
EXPECT_EQ(" 42", format("{0: }", 42.0l)); EXPECT_EQ(" 42.0", format("{0: }", 42.0l));
EXPECT_THROW_MSG(format("{0: ", 'c'), format_error, EXPECT_THROW_MSG(format("{0: ", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0: }", 'c'), format_error, EXPECT_THROW_MSG(format("{0: }", 'c'), format_error,
@ -1034,11 +1048,8 @@ TEST(FormatterTest, HashFlag) {
EXPECT_EQ("0x42", format("{0:#x}", 0x42ull)); EXPECT_EQ("0x42", format("{0:#x}", 0x42ull));
EXPECT_EQ("042", format("{0:#o}", 042ull)); EXPECT_EQ("042", format("{0:#o}", 042ull));
if (FMT_USE_GRISU) EXPECT_EQ("-42.0", format("{0:#}", -42.0));
EXPECT_EQ("-42.0", format("{0:#}", -42.0)); EXPECT_EQ("-42.0", format("{0:#}", -42.0l));
else
EXPECT_EQ("-42.0000", format("{0:#}", -42.0));
EXPECT_EQ("-42.0000", format("{0:#}", -42.0l));
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error, EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error, EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
@ -1057,8 +1068,8 @@ TEST(FormatterTest, ZeroFlag) {
EXPECT_EQ("00042", format("{0:05}", 42ul)); EXPECT_EQ("00042", format("{0:05}", 42ul));
EXPECT_EQ("-0042", format("{0:05}", -42ll)); EXPECT_EQ("-0042", format("{0:05}", -42ll));
EXPECT_EQ("00042", format("{0:05}", 42ull)); EXPECT_EQ("00042", format("{0:05}", 42ull));
EXPECT_EQ("-0042", format("{0:05}", -42.0)); EXPECT_EQ("-0042.0", format("{0:07}", -42.0));
EXPECT_EQ("-0042", format("{0:05}", -42.0l)); EXPECT_EQ("-0042.0", format("{0:07}", -42.0l));
EXPECT_THROW_MSG(format("{0:0", 'c'), format_error, EXPECT_THROW_MSG(format("{0:0", 'c'), format_error,
"missing '}' in format string"); "missing '}' in format string");
EXPECT_THROW_MSG(format("{0:05}", 'c'), format_error, EXPECT_THROW_MSG(format("{0:05}", 'c'), format_error,
@ -1433,7 +1444,7 @@ TEST(FormatterTest, FormatFloat) {
TEST(FormatterTest, FormatDouble) { TEST(FormatterTest, FormatDouble) {
check_unknown_types(1.2, "eEfFgGaA", "double"); 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.000000", format("{:f}", 0.0));
EXPECT_EQ("0", format("{:g}", 0.0)); EXPECT_EQ("0", format("{:g}", 0.0));
EXPECT_EQ("392.65", format("{:}", 392.65)); EXPECT_EQ("392.65", format("{:}", 392.65));
@ -1453,12 +1464,6 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ(buffer, format("{:A}", -42.0)); 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) { TEST(FormatterTest, FormatNaN) {
double nan = std::numeric_limits<double>::quiet_NaN(); double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", format("{}", nan)); EXPECT_EQ("nan", format("{}", nan));
@ -1483,7 +1488,7 @@ TEST(FormatterTest, FormatInfinity) {
} }
TEST(FormatterTest, FormatLongDouble) { 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("0.000000", format("{0:f}", 0.0l));
EXPECT_EQ("392.65", format("{0:}", 392.65l)); EXPECT_EQ("392.65", format("{0:}", 392.65l));
EXPECT_EQ("392.65", format("{0:g}", 392.65l)); EXPECT_EQ("392.65", format("{0:g}", 392.65l));
@ -1721,29 +1726,29 @@ TEST(FormatIntTest, FormatInt) {
} }
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push # pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif #endif
#if FMT_MSC_VER #if FMT_MSC_VER
#pragma warning(push) # pragma warning(push)
#pragma warning(disable: 4996) // Using a deprecated function # pragma warning(disable : 4996) // Using a deprecated function
#endif #endif
template <typename T> std::string format_decimal(T value) { template <typename T> std::string format_decimal(T value) {
char buffer[10]; char buffer[10];
char* ptr = buffer; char* ptr = buffer;
// TODO: Replace with safer, non-deprecated overload // TODO: Replace with safer, non-deprecated overload
fmt::format_decimal(ptr, value); fmt::format_decimal(ptr, value);
return std::string(buffer, ptr); return std::string(buffer, ptr);
} }
#if FMT_MSC_VER #if FMT_MSC_VER
#pragma warning(pop) # pragma warning(pop)
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
TEST(FormatIntTest, FormatDec) { TEST(FormatIntTest, FormatDec) {
@ -1781,13 +1786,13 @@ TEST(FormatTest, Dynamic) {
std::vector<fmt::basic_format_arg<ctx>> args; std::vector<fmt::basic_format_arg<ctx>> args;
args.emplace_back(fmt::internal::make_arg<ctx>(42)); args.emplace_back(fmt::internal::make_arg<ctx>(42));
args.emplace_back(fmt::internal::make_arg<ctx>("abc1")); args.emplace_back(fmt::internal::make_arg<ctx>("abc1"));
args.emplace_back(fmt::internal::make_arg<ctx>(1.2f)); args.emplace_back(fmt::internal::make_arg<ctx>(1.5f));
std::string result = fmt::vformat( std::string result = fmt::vformat(
"{} and {} and {}", fmt::basic_format_args<ctx>( "{} and {} and {}", fmt::basic_format_args<ctx>(
args.data(), static_cast<unsigned>(args.size()))); args.data(), static_cast<unsigned>(args.size())));
EXPECT_EQ("42 and abc1 and 1.2", result); EXPECT_EQ("42 and abc1 and 1.5", result);
} }
TEST(FormatTest, JoinArg) { TEST(FormatTest, JoinArg) {
@ -2376,8 +2381,8 @@ TEST(FormatTest, FormatStringErrors) {
EXPECT_ERROR("{:d}", "invalid type specifier", std::string); EXPECT_ERROR("{:d}", "invalid type specifier", std::string);
EXPECT_ERROR("{:s}", "invalid type specifier", void*); EXPECT_ERROR("{:s}", "invalid type specifier", void*);
# endif # endif
EXPECT_ERROR("{foo", EXPECT_ERROR("{foo", "compile-time checks don't support named arguments",
"compile-time checks don't support named arguments", int); int);
EXPECT_ERROR_NOARGS("{10000000000}", "number is too big"); EXPECT_ERROR_NOARGS("{10000000000}", "number is too big");
EXPECT_ERROR_NOARGS("{0x}", "invalid format string"); EXPECT_ERROR_NOARGS("{0x}", "invalid format string");
EXPECT_ERROR_NOARGS("{-}", "invalid format string"); EXPECT_ERROR_NOARGS("{-}", "invalid format string");

View File

@ -5,7 +5,6 @@
// //
// For the license information refer to format.h. // For the license information refer to format.h.
#define FMT_USE_GRISU std::numeric_limits<double>::is_iec559
#include "fmt/format.h" #include "fmt/format.h"
#include "gtest.h" #include "gtest.h"
@ -36,11 +35,21 @@ TEST(GrisuTest, Inf) {
EXPECT_EQ("-inf", fmt::format("{}", -inf)); EXPECT_EQ("-inf", fmt::format("{}", -inf));
} }
TEST(GrisuTest, Zero) { TEST(GrisuTest, Zero) { EXPECT_EQ("0.0", fmt::format("{}", 0.0)); }
EXPECT_EQ("0", fmt::format("{}", 0.0));
}
TEST(GrisuTest, Round) { TEST(GrisuTest, Round) {
EXPECT_EQ("1.9156918820264798e-56", EXPECT_EQ("1.9156918820264798e-56",
fmt::format("{}", 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));
}

View File

@ -368,8 +368,8 @@ TEST(PrintfTest, Length) {
TestLength<std::size_t>("z"); TestLength<std::size_t>("z");
TestLength<std::ptrdiff_t>("t"); TestLength<std::ptrdiff_t>("t");
long double max = std::numeric_limits<long double>::max(); long double max = std::numeric_limits<long double>::max();
EXPECT_PRINTF(fmt::format("{}", max), "%g", max); EXPECT_PRINTF(fmt::format("{:.6}", max), "%g", max);
EXPECT_PRINTF(fmt::format("{}", max), "%Lg", max); EXPECT_PRINTF(fmt::format("{:.6}", max), "%Lg", max);
} }
TEST(PrintfTest, Bool) { TEST(PrintfTest, Bool) {