Integrate Grisu
This commit is contained in:
parent
699297520a
commit
50b18a3c10
@ -68,13 +68,13 @@ include(CheckCXXCompilerFlag)
|
|||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||||
set(PEDANTIC_COMPILE_FLAGS -pedantic-errors -Wall -Wextra -pedantic
|
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
|
-Wredundant-decls -Wshadow -Wwrite-strings -Wpointer-arith
|
||||||
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
-Wcast-qual -Wformat=2 -Wmissing-include-dirs
|
||||||
-Wcast-align -Wnon-virtual-dtor
|
-Wcast-align -Wnon-virtual-dtor
|
||||||
-Wctor-dtor-privacy -Wdisabled-optimization
|
-Wctor-dtor-privacy -Wdisabled-optimization
|
||||||
-Winvalid-pch -Woverloaded-virtual
|
-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)
|
-Wno-format-nonliteral -Wno-sign-conversion -Wno-shadow)
|
||||||
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
if (NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.6)
|
||||||
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept)
|
set(PEDANTIC_COMPILE_FLAGS ${PEDANTIC_COMPILE_FLAGS} -Wnoexcept)
|
||||||
@ -93,7 +93,7 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
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-weak-vtables -Wno-padded -Wno-gnu-statement-expression
|
||||||
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
|
-Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-reserved-id-macro
|
||||||
-Wno-global-constructors -Wno-disabled-macro-expansion
|
-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]);
|
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_FUNC void grisu2_round(char *buffer, size_t size, uint64_t delta,
|
FMT_FUNC bool grisu2_round(
|
||||||
uint64_t remainder, uint64_t exp, uint64_t diff) {
|
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 &&
|
while (remainder < diff && delta - remainder >= exp &&
|
||||||
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
(remainder + exp < diff || diff - remainder > remainder + exp - diff)) {
|
||||||
--buffer[size - 1];
|
--buffer[size - 1];
|
||||||
remainder += exp;
|
remainder += exp;
|
||||||
}
|
}
|
||||||
|
if (size > max_digits) {
|
||||||
|
--size;
|
||||||
|
++exp10;
|
||||||
|
if (buffer[size] >= '5')
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generates output using Grisu2 digit-gen algorithm.
|
// Generates output using Grisu2 digit-gen algorithm.
|
||||||
FMT_FUNC void grisu2_gen_digits(
|
FMT_FUNC bool grisu2_gen_digits(
|
||||||
const fp &scaled_value, const fp &scaled_upper, uint64_t delta,
|
char *buffer, size_t &size, uint32_t hi, uint64_t lo, int &exp,
|
||||||
char *buffer, size_t &size, int &dec_exp) {
|
uint64_t delta, const fp &one, const fp &diff, size_t max_digits) {
|
||||||
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.
|
|
||||||
// Generate digits for the most significant part (hi).
|
// Generate digits for the most significant part (hi).
|
||||||
while (exp > 0) {
|
while (exp > 0) {
|
||||||
uint32_t digit = 0;
|
uint32_t digit = 0;
|
||||||
@ -507,12 +505,11 @@ FMT_FUNC void grisu2_gen_digits(
|
|||||||
buffer[size++] = static_cast<char>('0' + digit);
|
buffer[size++] = static_cast<char>('0' + digit);
|
||||||
--exp;
|
--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) {
|
if (remainder <= delta || size > max_digits) {
|
||||||
dec_exp += exp;
|
return grisu2_round(
|
||||||
grisu2_round(buffer, size, delta, remainder,
|
buffer, size, max_digits, delta, remainder,
|
||||||
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
static_cast<uint64_t>(data::POWERS_OF_10_32[exp]) << -one.e,
|
||||||
diff.f);
|
diff.f, exp);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Generate digits for the least significant part (lo).
|
// 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);
|
buffer[size++] = static_cast<char>('0' + digit);
|
||||||
lo &= one.f - 1;
|
lo &= one.f - 1;
|
||||||
--exp;
|
--exp;
|
||||||
if (lo < delta) {
|
if (lo < delta || size > max_digits) {
|
||||||
dec_exp += exp;
|
return grisu2_round(buffer, size, max_digits, delta, lo, one.f,
|
||||||
grisu2_round(buffer, size, delta, lo, one.f,
|
diff.f * data::POWERS_OF_10_32[-exp], exp);
|
||||||
diff.f * data::POWERS_OF_10_32[-exp]);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
|
||||||
FMT_FUNC char *write_exponent(char *buffer, int exp) {
|
FMT_FUNC char *write_exponent(char *buffer, int exp) {
|
||||||
FMT_ASSERT(-1000 < exp && exp < 1000, "exponent out of range");
|
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;
|
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
|
#if FMT_CLANG_VERSION
|
||||||
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
# define FMT_FALLTHROUGH [[clang::fallthrough]];
|
||||||
#elif FMT_GCC_VERSION >= 700
|
#elif FMT_GCC_VERSION >= 700
|
||||||
@ -659,60 +559,159 @@ FMT_FUNC void grisu2_prettify(char *buffer, size_t &size, int exp,
|
|||||||
# define FMT_FALLTHROUGH
|
# define FMT_FALLTHROUGH
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Formats a nonnegative value using Grisu2 algorithm. Grisu2 doesn't give any
|
struct gen_digits_params {
|
||||||
// guarantees on the shortness of the result.
|
unsigned min_digits;
|
||||||
template <typename Double>
|
unsigned max_digits;
|
||||||
FMT_FUNC typename std::enable_if<sizeof(Double) == sizeof(uint64_t)>::type
|
bool fixed;
|
||||||
grisu2_format(Double value, char *buffer, size_t &size, char type,
|
bool upper;
|
||||||
int precision, bool write_decimal_point) {
|
bool trailing_zeros;
|
||||||
FMT_ASSERT(value >= 0, "value is negative");
|
|
||||||
int dec_exp = 0; // K in Grisu.
|
// Creates digit generation parameters from format specifiers for a number in
|
||||||
if (value > 0) {
|
// the range [pow(10, exp - 1), pow(10, exp) or 0 if exp == 1.
|
||||||
grisu2_format_positive(value, buffer, size, dec_exp);
|
gen_digits_params(const grisu2_specs &specs, int exp)
|
||||||
} else {
|
: min_digits(specs.precision >= 0 ? to_unsigned(specs.precision) : 6),
|
||||||
*buffer = '0';
|
fixed(false), upper(false), trailing_zeros(false) {
|
||||||
size = 1;
|
switch (specs.type) {
|
||||||
}
|
case 'G':
|
||||||
const int default_precision = 6;
|
upper = true;
|
||||||
if (precision < 0)
|
FMT_FALLTHROUGH
|
||||||
precision = default_precision;
|
case '\0': case 'g':
|
||||||
bool upper = false;
|
trailing_zeros = (specs.flags & HASH_FLAG) != 0;
|
||||||
switch (type) {
|
if (-4 <= exp && exp < static_cast<int>(min_digits) + 1) {
|
||||||
case 'G':
|
fixed = true;
|
||||||
upper = true;
|
if (!specs.type && trailing_zeros && exp >= 0)
|
||||||
FMT_FALLTHROUGH
|
min_digits = to_unsigned(exp) + 1;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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;
|
case 'E':
|
||||||
break;
|
upper = true;
|
||||||
}
|
FMT_FALLTHROUGH
|
||||||
case 'F':
|
case 'e':
|
||||||
upper = true;
|
++min_digits;
|
||||||
FMT_FALLTHROUGH
|
break;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
if (write_decimal_point && precision < 1)
|
// pow(10, full_exp - 1) <= v <= pow(10, full_exp).
|
||||||
precision = 1;
|
int int_size = static_cast<int>(size);
|
||||||
grisu2_prettify(buffer, size, dec_exp, precision, upper);
|
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
|
} // namespace internal
|
||||||
|
|
||||||
|
@ -289,15 +289,20 @@ inline bool use_grisu() {
|
|||||||
return FMT_USE_GRISU && std::numeric_limits<double>::is_iec559;
|
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:
|
// Formats value using Grisu2 algorithm:
|
||||||
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
|
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
|
||||||
template <typename Double>
|
template <typename Double>
|
||||||
FMT_API typename std::enable_if<sizeof(Double) == sizeof(uint64_t)>::type
|
FMT_API typename std::enable_if<sizeof(Double) == sizeof(uint64_t), bool>::type
|
||||||
grisu2_format(Double value, char *buffer, size_t &size, char type,
|
grisu2_format(Double value, char *buffer, size_t &size, grisu2_specs);
|
||||||
int precision, bool write_decimal_point);
|
|
||||||
template <typename Double>
|
template <typename Double>
|
||||||
inline typename std::enable_if<sizeof(Double) != sizeof(uint64_t)>::type
|
inline typename std::enable_if<sizeof(Double) != sizeof(uint64_t), bool>::type
|
||||||
grisu2_format(Double, char *, size_t &, char, int, bool) {}
|
grisu2_format(Double, char *, size_t &, grisu2_specs) { return false; }
|
||||||
|
|
||||||
template <typename Allocator>
|
template <typename Allocator>
|
||||||
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
|
typename Allocator::value_type *allocate(Allocator& alloc, std::size_t n) {
|
||||||
@ -1203,13 +1208,13 @@ struct align_spec : empty_spec {
|
|||||||
template <typename Char>
|
template <typename Char>
|
||||||
class basic_format_specs : public align_spec {
|
class basic_format_specs : public align_spec {
|
||||||
public:
|
public:
|
||||||
unsigned flags_;
|
|
||||||
int precision_;
|
int precision_;
|
||||||
|
uint_least8_t flags_;
|
||||||
char type_;
|
char type_;
|
||||||
|
|
||||||
FMT_CONSTEXPR basic_format_specs(
|
FMT_CONSTEXPR basic_format_specs(
|
||||||
unsigned width = 0, char type = 0, wchar_t fill = ' ')
|
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 bool flag(unsigned f) const { return (flags_ & f) != 0; }
|
||||||
FMT_CONSTEXPR int precision() const { return precision_; }
|
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;
|
memory_buffer buffer;
|
||||||
char type = static_cast<char>(spec.type());
|
char type = static_cast<char>(spec.type());
|
||||||
if (internal::const_check(
|
bool use_grisu = internal::use_grisu() && sizeof(T) <= sizeof(double) &&
|
||||||
internal::use_grisu() && sizeof(T) <= sizeof(double)) &&
|
type != 'a' && type != 'A';
|
||||||
type != 'a' && type != 'A') {
|
if (use_grisu) {
|
||||||
char buf[100]; // TODO: correct buffer size
|
char buf[100]; // TODO: correct buffer size
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
internal::grisu2_format(static_cast<double>(value), buf, size, type,
|
auto gs = internal::grisu2_specs();
|
||||||
spec.precision(), spec.flag(HASH_FLAG));
|
gs.type = type;
|
||||||
FMT_ASSERT(size <= 100, "buffer overflow");
|
gs.precision = spec.precision();
|
||||||
buffer.append(buf, buf + size); // TODO: avoid extra copy
|
gs.flags = spec.flags_;
|
||||||
} else {
|
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);
|
format_specs normalized_spec(spec);
|
||||||
normalized_spec.type_ = handler.type;
|
normalized_spec.type_ = handler.type;
|
||||||
write_double_sprintf(value, normalized_spec, buffer);
|
write_double_sprintf(value, normalized_spec, buffer);
|
||||||
|
@ -104,7 +104,7 @@ TEST(FPTest, GetCachedPower) {
|
|||||||
|
|
||||||
TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
|
TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) {
|
||||||
size_t size = 0;
|
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>
|
template <typename T>
|
||||||
|
@ -180,13 +180,8 @@ TEST(PrintfTest, HashFlag) {
|
|||||||
safe_sprintf(buffer, "%#E", -42.0);
|
safe_sprintf(buffer, "%#E", -42.0);
|
||||||
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
EXPECT_PRINTF(buffer, "%#E", -42.0);
|
||||||
|
|
||||||
if (fmt::internal::use_grisu()) {
|
EXPECT_PRINTF("-42.0000", "%#g", -42.0);
|
||||||
EXPECT_PRINTF("-42.0", "%#g", -42.0);
|
EXPECT_PRINTF("-42.0000", "%#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);
|
|
||||||
}
|
|
||||||
|
|
||||||
safe_sprintf(buffer, "%#a", 16.0);
|
safe_sprintf(buffer, "%#a", 16.0);
|
||||||
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
EXPECT_PRINTF(buffer, "%#a", 16.0);
|
||||||
|
Loading…
Reference in New Issue
Block a user