mirror of
https://github.com/fmtlib/fmt.git
synced 2024-11-26 12:10:06 +00:00
Integrate Grisu
This commit is contained in:
parent
699297520a
commit
50b18a3c10
@ -68,13 +68,13 @@ include(CheckCXXCompilerFlag)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
||||
-Wold-style-cast -Wfloat-equal -Wlogical-op -Wundef
|
||||
-Wold-style-cast -Wlogical-op -Wundef
|
||||
-Wredundant-decls -Wshadow -Wwrite-strings -Wpointer-arith
|
||||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||
-Wcast-align -Wnon-virtual-dtor
|
||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||
-Winvalid-pch -Woverloaded-virtual
|
||||
-Wno-ctor-dtor-privacy -Wno-dangling-else -Wno-float-equal
|
||||
-Wno-ctor-dtor-privacy -Wno-dangling-else
|
||||
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow)
|
||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept)
|
||||
@ -93,7 +93,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(PEDANTIC_COMPILE_FLAGS -Weverything -Wpedantic
|
||||
set(PEDANTIC_COMPILE_FLAGS -Wall -Wextra -Wpedantic
|
||||
-Wno-weak-vtables -Wno-padded -Wno-gnu-statement-expression
|
||||
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
|
||||
-Wno-global-constructors -Wno-disabled-macro-expansion
|
||||
|
@ -461,29 +461,27 @@ FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||
}
|
||||
|
||||
FMT_FUNC void grisu2_round(char *buffer, size_t size, uint64_t delta,
|
||||
uint64_t remainder, uint64_t exp, uint64_t diff) {
|
||||
FMT_FUNC bool grisu2_round(
|
||||
char *buffer, size_t &size, size_t max_digits, uint64_t delta,
|
||||
uint64_t remainder, uint64_t exp, uint64_t diff, int &exp10) {
|
||||
while (remainder < diff && delta - remainder >= exp &&
|
||||
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
||||
--buffer[size - 1];
|
||||
remainder += exp;
|
||||
}
|
||||
if (size > max_digits) {
|
||||
--size;
|
||||
++exp10;
|
||||
if (buffer[size] >= '5')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Generates output using Grisu2 digit-gen algorithm.
|
||||
FMT_FUNC void grisu2_gen_digits(
|
||||
const fp &scaled_value, const fp &scaled_upper, uint64_t delta,
|
||||
char *buffer, size_t &size, int &dec_exp) {
|
||||
internal::fp one(1ull << -scaled_upper.e, scaled_upper.e);
|
||||
internal::fp diff = scaled_upper - scaled_value; // wp_w in Grisu.
|
||||
// hi (p1 in Grisu) contains the most significant digits of scaled_upper.
|
||||
// hi = floor(scaled_upper / one).
|
||||
uint32_t hi = static_cast<uint32_t>(scaled_upper.f >> -one.e);
|
||||
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||
// lo = scaled_upper % one.
|
||||
uint64_t lo = scaled_upper.f & (one.f - 1);
|
||||
size = 0;
|
||||
auto exp = count_digits(hi); // kappa in Grisu.
|
||||
FMT_FUNC bool grisu2_gen_digits(
|
||||
char *buffer, size_t &size, uint32_t hi, uint64_t lo, int &exp,
|
||||
uint64_t delta, const fp &one, const fp &diff, size_t max_digits) {
|
||||
// Generate digits for the most significant part (hi).
|
||||
while (exp > 0) {
|
||||
uint32_t digit = 0;
|
||||
@ -507,12 +505,11 @@ FMT_FUNC void grisu2_gen_digits(
|
||||
buffer[size++] = static_cast<char>('0' + digit);
|
||||
--exp;
|
||||
uint64_t remainder = (static_cast<uint64_t>(hi) << -one.e) + lo;
|
||||
if (remainder <= delta) {
|
||||
dec_exp += exp;
|
||||
grisu2_round(buffer, size, delta, remainder,
|
||||
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||
diff.f);
|
||||
return;
|
||||
if (remainder <= delta || size > max_digits) {
|
||||
return grisu2_round(
|
||||
buffer, size, max_digits, delta, remainder,
|
||||
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||
diff.f, exp);
|
||||
}
|
||||
}
|
||||
// Generate digits for the least significant part (lo).
|
||||
@ -524,49 +521,13 @@ FMT_FUNC void grisu2_gen_digits(
|
||||
buffer[size++] = static_cast<char>('0' + digit);
|
||||
lo &= one.f - 1;
|
||||
--exp;
|
||||
if (lo < delta) {
|
||||
dec_exp += exp;
|
||||
grisu2_round(buffer, size, delta, lo, one.f,
|
||||
diff.f * data::POWERS_OF_10_32[-exp]);
|
||||
return;
|
||||
if (lo < delta || size > max_digits) {
|
||||
return grisu2_round(buffer, size, max_digits, delta, lo, one.f,
|
||||
diff.f * data::POWERS_OF_10_32[-exp], exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Double>
|
||||
FMT_FUNC void grisu2_format_positive(Double value, char *buffer, size_t &size,
|
||||
int &dec_exp) {
|
||||
FMT_ASSERT(value > 0, "value is nonpositive");
|
||||
fp fp_value(value);
|
||||
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||
fp_value.compute_boundaries(lower, upper);
|
||||
// Find a cached power of 10 close to 1 / upper.
|
||||
const int min_exp = -60; // alpha in Grisu.
|
||||
auto dec_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||
min_exp - (upper.e + fp::significand_size), dec_exp);
|
||||
dec_exp = -dec_exp;
|
||||
fp_value.normalize();
|
||||
fp scaled_value = fp_value * dec_pow;
|
||||
fp scaled_lower = lower * dec_pow; // \tilde{M}^- in Grisu.
|
||||
fp scaled_upper = upper * dec_pow; // \tilde{M}^+ in Grisu.
|
||||
++scaled_lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||
--scaled_upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||
uint64_t delta = scaled_upper.f - scaled_lower.f;
|
||||
grisu2_gen_digits(scaled_value, scaled_upper, delta, buffer, size, dec_exp);
|
||||
}
|
||||
|
||||
FMT_FUNC void round(char *buffer, size_t &size, int &exp,
|
||||
int digits_to_remove) {
|
||||
size -= to_unsigned(digits_to_remove);
|
||||
exp += digits_to_remove;
|
||||
int digit = buffer[size] - '0';
|
||||
// TODO: proper rounding and carry
|
||||
if (digit > 5 || (digit == 5 && (digits_to_remove > 1 ||
|
||||
(buffer[size - 1] - '0') % 2) != 0)) {
|
||||
++buffer[size - 1];
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||
FMT_FUNC char *write_exponent(char *buffer, int exp) {
|
||||
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
||||
@ -590,67 +551,6 @@ FMT_FUNC char *write_exponent(char *buffer, int exp) {
|
||||
return buffer;
|
||||
}
|
||||
|
||||
FMT_FUNC void format_exp_notation(
|
||||
char *buffer, size_t &size, int exp, int precision, bool upper) {
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
std::memmove(buffer + 2, buffer + 1, size - 1);
|
||||
buffer[1] = '.';
|
||||
exp += static_cast<int>(size) - 1;
|
||||
int num_digits = precision - static_cast<int>(size) + 1;
|
||||
if (num_digits > 0) {
|
||||
std::uninitialized_fill_n(buffer + size + 1, num_digits, '0');
|
||||
size += to_unsigned(num_digits);
|
||||
} else if (num_digits < 0) {
|
||||
round(buffer, size, exp, -num_digits);
|
||||
}
|
||||
char *p = buffer + size + 1;
|
||||
*p++ = upper ? 'E' : 'e';
|
||||
size = to_unsigned(write_exponent(p, exp) - buffer);
|
||||
}
|
||||
|
||||
// Prettifies the output of the Grisu2 algorithm.
|
||||
// The number is given as v = buffer * 10^exp.
|
||||
FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
|
||||
int precision, bool upper) {
|
||||
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||
int int_size = static_cast<int>(size);
|
||||
int full_exp = int_size + exp;
|
||||
const int exp_threshold = 21;
|
||||
if (int_size <= full_exp && full_exp <= exp_threshold) {
|
||||
// 1234e7 -> 12340000000[.0+]
|
||||
std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
|
||||
char *p = buffer + full_exp;
|
||||
if (precision > 0) {
|
||||
*p++ = '.';
|
||||
std::uninitialized_fill_n(p, precision, '0');
|
||||
p += precision;
|
||||
}
|
||||
size = to_unsigned(p - buffer);
|
||||
} else if (0 < full_exp && full_exp <= exp_threshold) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int fractional_size = -exp;
|
||||
std::memmove(buffer + full_exp + 1, buffer + full_exp,
|
||||
to_unsigned(fractional_size));
|
||||
buffer[full_exp] = '.';
|
||||
int num_zeros = precision - fractional_size;
|
||||
if (num_zeros > 0) {
|
||||
std::uninitialized_fill_n(buffer + size + 1, num_zeros, '0');
|
||||
size += to_unsigned(num_zeros);
|
||||
}
|
||||
++size;
|
||||
} else if (-6 < full_exp && full_exp <= 0) {
|
||||
// 1234e-6 -> 0.001234
|
||||
int offset = 2 - full_exp;
|
||||
std::memmove(buffer + offset, buffer, size);
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
|
||||
size = to_unsigned(int_size + offset);
|
||||
} else {
|
||||
format_exp_notation(buffer, size, exp, precision, upper);
|
||||
}
|
||||
}
|
||||
|
||||
#if FMT_CLANG_VERSION
|
||||
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||
#elif FMT_GCC_VERSION >= 700
|
||||
@ -659,60 +559,159 @@ FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
|
||||
# define FMT_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any
|
||||
// guarantees on the shortness of the result.
|
||||
template <typename Double>
|
||||
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t)>::type
|
||||
grisu2_format(Double value, char *buffer, size_t &size, char type,
|
||||
int precision, bool write_decimal_point) {
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
int dec_exp = 0; // K in Grisu.
|
||||
if (value > 0) {
|
||||
grisu2_format_positive(value, buffer, size, dec_exp);
|
||||
} else {
|
||||
*buffer = '0';
|
||||
size = 1;
|
||||
}
|
||||
const int default_precision = 6;
|
||||
if (precision < 0)
|
||||
precision = default_precision;
|
||||
bool upper = false;
|
||||
switch (type) {
|
||||
case 'G':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH
|
||||
case '\0': case 'g': {
|
||||
int digits_to_remove = static_cast<int>(size) - precision;
|
||||
if (digits_to_remove > 0) {
|
||||
round(buffer, size, dec_exp, digits_to_remove);
|
||||
// Remove trailing zeros.
|
||||
while (size > 0 && buffer[size - 1] == '0') {
|
||||
--size;
|
||||
++dec_exp;
|
||||
struct gen_digits_params {
|
||||
unsigned min_digits;
|
||||
unsigned max_digits;
|
||||
bool fixed;
|
||||
bool upper;
|
||||
bool trailing_zeros;
|
||||
|
||||
// Creates digit generation parameters from format specifiers for a number in
|
||||
// the range [pow(10, exp - 1), pow(10, exp) or 0 if exp == 1.
|
||||
gen_digits_params(const grisu2_specs &specs, int exp)
|
||||
: min_digits(specs.precision >= 0 ? to_unsigned(specs.precision) : 6),
|
||||
fixed(false), upper(false), trailing_zeros(false) {
|
||||
switch (specs.type) {
|
||||
case 'G':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH
|
||||
case '\0': case 'g':
|
||||
trailing_zeros = (specs.flags & HASH_FLAG) != 0;
|
||||
if (-4 <= exp && exp < static_cast<int>(min_digits) + 1) {
|
||||
fixed = true;
|
||||
if (!specs.type && trailing_zeros && exp >= 0)
|
||||
min_digits = to_unsigned(exp) + 1;
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH
|
||||
case 'f': {
|
||||
fixed = true;
|
||||
trailing_zeros = true;
|
||||
int adjusted_min_digits = static_cast<int>(min_digits) + exp;
|
||||
if (adjusted_min_digits > 0)
|
||||
min_digits = to_unsigned(adjusted_min_digits);
|
||||
break;
|
||||
}
|
||||
precision = 0;
|
||||
break;
|
||||
}
|
||||
case 'F':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH
|
||||
case 'f': {
|
||||
int digits_to_remove = -dec_exp - precision;
|
||||
if (digits_to_remove > 0) {
|
||||
if (digits_to_remove >= static_cast<int>(size))
|
||||
digits_to_remove = static_cast<int>(size) - 1;
|
||||
round(buffer, size, dec_exp, digits_to_remove);
|
||||
case 'E':
|
||||
upper = true;
|
||||
FMT_FALLTHROUGH
|
||||
case 'e':
|
||||
++min_digits;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
max_digits = min_digits;
|
||||
}
|
||||
case 'e': case 'E':
|
||||
format_exp_notation(buffer, size, dec_exp, precision, type == 'E');
|
||||
};
|
||||
|
||||
// The number is given as v = buffer * pow(10, exp).
|
||||
FMT_FUNC void format_float(char *buffer, size_t &size, int exp,
|
||||
const gen_digits_params ¶ms) {
|
||||
if (!params.fixed) {
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
std::memmove(buffer + 2, buffer + 1, size - 1);
|
||||
buffer[1] = '.';
|
||||
exp += static_cast<int>(size) - 1;
|
||||
if (size < params.min_digits) {
|
||||
std::uninitialized_fill_n(buffer + size + 1,
|
||||
params.min_digits - size, '0');
|
||||
size = params.min_digits;
|
||||
}
|
||||
char *p = buffer + size + 1;
|
||||
*p++ = params.upper ? 'E' : 'e';
|
||||
size = to_unsigned(write_exponent(p, exp) - buffer);
|
||||
return;
|
||||
}
|
||||
if (write_decimal_point && precision < 1)
|
||||
precision = 1;
|
||||
grisu2_prettify(buffer, size, dec_exp, precision, upper);
|
||||
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||
int int_size = static_cast<int>(size);
|
||||
int full_exp = int_size + exp;
|
||||
const int exp_threshold = 21;
|
||||
if (int_size <= full_exp && full_exp <= exp_threshold) {
|
||||
// 1234e7 -> 12340000000[.0+]
|
||||
std::uninitialized_fill_n(buffer + int_size, full_exp - int_size, '0');
|
||||
char *p = buffer + full_exp;
|
||||
int num_zeros = static_cast<int>(params.min_digits) - full_exp;
|
||||
if (num_zeros > 0 && params.trailing_zeros) {
|
||||
*p++ = '.';
|
||||
std::uninitialized_fill_n(p, num_zeros, '0');
|
||||
p += num_zeros;
|
||||
}
|
||||
size = to_unsigned(p - buffer);
|
||||
} else if (full_exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int fractional_size = -exp;
|
||||
std::memmove(buffer + full_exp + 1, buffer + full_exp,
|
||||
to_unsigned(fractional_size));
|
||||
buffer[full_exp] = '.';
|
||||
++size;
|
||||
if (!params.trailing_zeros) {
|
||||
// Remove trailing zeros.
|
||||
while (buffer[size - 1] == '0') --size;
|
||||
} else if (params.min_digits >= size) {
|
||||
// Add trailing zeros.
|
||||
size_t num_zeros = params.min_digits - size + 1;
|
||||
std::uninitialized_fill_n(buffer + size, num_zeros, '0');
|
||||
size += to_unsigned(num_zeros);
|
||||
}
|
||||
} else {
|
||||
// 1234e-6 -> 0.001234
|
||||
int offset = 2 - full_exp;
|
||||
std::memmove(buffer + offset, buffer, size);
|
||||
buffer[0] = '0';
|
||||
buffer[1] = '.';
|
||||
std::uninitialized_fill_n(buffer + 2, -full_exp, '0');
|
||||
size = to_unsigned(int_size + offset);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Double>
|
||||
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||
grisu2_format(Double value, char *buffer, size_t &size,
|
||||
grisu2_specs specs) {
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
if (value == 0) {
|
||||
gen_digits_params params(specs, 1);
|
||||
*buffer = '0';
|
||||
size = 1;
|
||||
format_float(buffer, size, 0, params);
|
||||
return true;
|
||||
}
|
||||
|
||||
fp fp_value(value);
|
||||
fp lower, upper; // w^- and w^+ in the Grisu paper.
|
||||
fp_value.compute_boundaries(lower, upper);
|
||||
|
||||
// Find a cached power of 10 close to 1 / upper and use it to scale upper.
|
||||
const int min_exp = -60; // alpha in Grisu.
|
||||
int cached_exp = 0; // K in Grisu.
|
||||
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||
min_exp - (upper.e + fp::significand_size), cached_exp);
|
||||
cached_exp = -cached_exp;
|
||||
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||
fp one(1ull << -upper.e, upper.e);
|
||||
// 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);
|
||||
int exp = static_cast<int>(count_digits(hi)); // kappa in Grisu.
|
||||
gen_digits_params params(specs, cached_exp + exp);
|
||||
fp_value.normalize();
|
||||
fp scaled_value = fp_value * cached_pow;
|
||||
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
|
||||
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||
uint64_t delta = upper.f - lower.f;
|
||||
fp diff = upper - scaled_value; // wp_w in Grisu.
|
||||
// lo (p2 in Grisu) contains the least significants digits of scaled_upper.
|
||||
// lo = supper % one.
|
||||
uint64_t lo = upper.f & (one.f - 1);
|
||||
size = 0;
|
||||
if (!grisu2_gen_digits(buffer, size, hi, lo, exp, delta, one, diff,
|
||||
params.max_digits)) {
|
||||
return false;
|
||||
}
|
||||
format_float(buffer, size, cached_exp + exp, params);
|
||||
return true;
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
|
@ -289,15 +289,20 @@ inline bool use_grisu() {
|
||||
return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559;
|
||||
}
|
||||
|
||||
struct grisu2_specs {
|
||||
int precision;
|
||||
char type;
|
||||
uint_least8_t flags;
|
||||
};
|
||||
|
||||
// Formats value using Grisu2 algorithm:
|
||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
|
||||
template <typename Double>
|
||||
FMT_API typename std::enable_if<sizeof(Double) == sizeof(uint64_t)>::type
|
||||
grisu2_format(Double value, char *buffer, size_t &size, char type,
|
||||
int precision, bool write_decimal_point);
|
||||
FMT_API typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||
grisu2_format(Double value, char *buffer, size_t &size, grisu2_specs);
|
||||
template <typename Double>
|
||||
inline typename std::enable_if<sizeof(Double) != sizeof(uint64_t)>::type
|
||||
grisu2_format(Double, char *, size_t &, char, int, bool) {}
|
||||
inline typename std::enable_if<sizeof(Double) != sizeof(uint64_t), bool>::type
|
||||
grisu2_format(Double, char *, size_t &, grisu2_specs) { return false; }
|
||||
|
||||
template <typename Allocator>
|
||||
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
|
||||
@ -1203,13 +1208,13 @@ struct align_spec : empty_spec {
|
||||
template <typename Char>
|
||||
class basic_format_specs : public align_spec {
|
||||
public:
|
||||
unsigned flags_;
|
||||
int precision_;
|
||||
uint_least8_t flags_;
|
||||
char type_;
|
||||
|
||||
FMT_CONSTEXPR basic_format_specs(
|
||||
unsigned width = 0, char type = 0, wchar_t fill = ' ')
|
||||
: align_spec(width, fill), flags_(0), precision_(-1), type_(type) {}
|
||||
: align_spec(width, fill), precision_(-1), flags_(0), type_(type) {}
|
||||
|
||||
FMT_CONSTEXPR bool flag(unsigned f) const { return (flags_ & f) != 0; }
|
||||
FMT_CONSTEXPR int precision() const { return precision_; }
|
||||
@ -2881,16 +2886,23 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
|
||||
memory_buffer buffer;
|
||||
char type = static_cast<char>(spec.type());
|
||||
if (internal::const_check(
|
||||
internal::use_grisu() && sizeof(T) <= sizeof(double)) &&
|
||||
type != 'a' && type != 'A') {
|
||||
bool use_grisu = internal::use_grisu() && sizeof(T) <= sizeof(double) &&
|
||||
type != 'a' && type != 'A';
|
||||
if (use_grisu) {
|
||||
char buf[100]; // TODO: correct buffer size
|
||||
size_t size = 0;
|
||||
internal::grisu2_format(static_cast<double>(value), buf, size, type,
|
||||
spec.precision(), spec.flag(HASH_FLAG));
|
||||
FMT_ASSERT(size <= 100, "buffer overflow");
|
||||
buffer.append(buf, buf + size); // TODO: avoid extra copy
|
||||
} else {
|
||||
auto gs = internal::grisu2_specs();
|
||||
gs.type = type;
|
||||
gs.precision = spec.precision();
|
||||
gs.flags = spec.flags_;
|
||||
use_grisu = internal::grisu2_format(
|
||||
static_cast<double>(value), buf, size, gs);
|
||||
if (use_grisu) {
|
||||
FMT_ASSERT(size <= 100, "buffer overflow");
|
||||
buffer.append(buf, buf + size); // TODO: avoid extra copy
|
||||
}
|
||||
}
|
||||
if (!use_grisu) {
|
||||
format_specs normalized_spec(spec);
|
||||
normalized_spec.type_ = handler.type;
|
||||
write_double_sprintf(value, normalized_spec, buffer);
|
||||
|
@ -104,7 +104,7 @@ TEST(FPTest, GetCachedPower) {
|
||||
|
||||
TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
|
||||
size_t size = 0;
|
||||
fmt::internal::grisu2_format(4.2f, FMT_NULL, size, 0, 0, false);
|
||||
grisu2_format(4.2f, FMT_NULL, size, fmt::internal::grisu2_specs());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
@ -180,13 +180,8 @@ TEST(PrintfTest, HashFlag) {
|
||||
safe_sprintf(buffer, "%#E", -42.0);
|
||||
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
||||
|
||||
if (fmt::internal::use_grisu()) {
|
||||
EXPECT_PRINTF("-42.0", "%#g", -42.0);
|
||||
EXPECT_PRINTF("-42.0", "%#G", -42.0);
|
||||
} else {
|
||||
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
||||
}
|
||||
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||
EXPECT_PRINTF("-42.0000", "%#G", -42.0);
|
||||
|
||||
safe_sprintf(buffer, "%#a", 16.0);
|
||||
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
||||
|
Loading…
Reference in New Issue
Block a user