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>
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<Rep>::value ? "{:g}" : "{}",
val);
}
return format_to(out, "{:.{}f}", val, precision);
}
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,
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<uint64_t>(hi) << -one.e) + lo;
if (remainder <= delta || size > max_digits) {
return grisu2_round(buf, size, max_digits, delta, remainder,
static_cast<uint64_t>(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 <typename Handler> 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 <typename Handler>
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 <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>
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");
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<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 = 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<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 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<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);
break; // The buffer is large enough - continue with formatting.
}

View File

@ -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<double>::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 <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
// undefined behavior (e.g. due to type aliasing).
// 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) {}
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<Range>::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<T>() && !spec.type && !spec.has_precision() &&
internal::grisu2_format(static_cast<double>(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) {

View File

@ -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))

View File

@ -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));
}

View File

@ -635,8 +635,15 @@ TEST(WriterTest, WriteLongLong) {
TEST(WriterTest, WriteDouble) {
CHECK_WRITE(4.2);
CHECK_WRITE(-4.2);
CHECK_WRITE(std::numeric_limits<double>::min());
CHECK_WRITE(std::numeric_limits<double>::max());
auto min = std::numeric_limits<double>::min();
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) {
@ -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<long double>::min());
CHECK_WRITE(std::numeric_limits<long double>::max());
auto min = std::numeric_limits<long double>::min();
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) {
@ -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<void*>(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<void*>(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<void*>(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<void*>(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<void*>(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<double>::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,13 +1726,13 @@ 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 <typename T> std::string format_decimal(T value) {
@ -1739,11 +1744,11 @@ template <typename T> std::string format_decimal(T value) {
}
#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<fmt::basic_format_arg<ctx>> args;
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>(1.2f));
args.emplace_back(fmt::internal::make_arg<ctx>(1.5f));
std::string result = fmt::vformat(
"{} and {} and {}", fmt::basic_format_args<ctx>(
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) {
@ -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");

View File

@ -5,7 +5,6 @@
//
// For the license information refer to format.h.
#define FMT_USE_GRISU std::numeric_limits<double>::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));
}

View File

@ -368,8 +368,8 @@ TEST(PrintfTest, Length) {
TestLength<std::size_t>("z");
TestLength<std::ptrdiff_t>("t");
long double max = std::numeric_limits<long double>::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) {