Enable grisu for shortest roundtrip (default) formatting
This commit is contained in:
parent
b8d34e0db3
commit
355eb6d29a
@ -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>
|
||||
|
@ -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.
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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))
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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,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 <typename T> 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<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");
|
||||
|
@ -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));
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user