mirror of
https://github.com/fmtlib/fmt.git
synced 2025-11-28 21:30:00 +01:00
Partially implement Grisu3
This commit is contained in:
@@ -494,10 +494,12 @@ enum result {
|
||||
};
|
||||
}
|
||||
|
||||
// Generates output using Grisu2 digit-gen algorithm.
|
||||
// Generates output using the Grisu digit-gen algorithm.
|
||||
// error: the size of the region (lower, upper) outside of which numbers
|
||||
// definitely do not round to value (Delta in Grisu3).
|
||||
template <typename Handler>
|
||||
digits::result grisu2_gen_digits(fp value, uint64_t error, int& exp,
|
||||
Handler& handler) {
|
||||
digits::result grisu_gen_digits(fp value, uint64_t error, int& exp,
|
||||
Handler& handler) {
|
||||
fp one(1ull << -value.e, value.e);
|
||||
// The integral part of scaled value (p1 in Grisu) = value / one. It cannot be
|
||||
// zero because it contains a product of two 64-bit numbers with MSB set (due
|
||||
@@ -635,34 +637,57 @@ struct fixed_handler {
|
||||
};
|
||||
|
||||
// The shortest representation digit handler.
|
||||
struct shortest_handler {
|
||||
template <int GRISU_VERSION> struct grisu_shortest_handler {
|
||||
char* buf;
|
||||
int size;
|
||||
fp diff; // wp_w in Grisu.
|
||||
// Distance between scaled value and upper bound (wp_W in Grisu3).
|
||||
uint64_t diff;
|
||||
|
||||
digits::result on_start(uint64_t, uint64_t, uint64_t, int&) {
|
||||
return digits::more;
|
||||
}
|
||||
|
||||
// This implements Grisu3's round_weed.
|
||||
digits::result on_digit(char digit, uint64_t divisor, uint64_t remainder,
|
||||
uint64_t error, int exp, bool integral) {
|
||||
buf[size++] = digit;
|
||||
if (remainder > error) return digits::more;
|
||||
uint64_t d = integral ? diff.f : diff.f * data::POWERS_OF_10_64[-exp];
|
||||
while (
|
||||
remainder < d && error - remainder >= divisor &&
|
||||
(remainder + divisor < d || d - remainder > remainder + divisor - d)) {
|
||||
if (remainder >= error) return digits::more;
|
||||
if (GRISU_VERSION != 3) {
|
||||
uint64_t d = integral ? diff : diff * data::POWERS_OF_10_64[-exp];
|
||||
while (remainder < d && error - remainder >= divisor &&
|
||||
(remainder + divisor < d ||
|
||||
d - remainder > remainder + divisor - d)) {
|
||||
--buf[size - 1];
|
||||
remainder += divisor;
|
||||
}
|
||||
return digits::done;
|
||||
}
|
||||
uint64_t unit = integral ? 1 : data::POWERS_OF_10_64[-exp];
|
||||
uint64_t up = diff + unit; // wp_Wup
|
||||
while (remainder < up && error - remainder >= divisor &&
|
||||
(remainder + divisor < up ||
|
||||
up - remainder > remainder + divisor - up)) {
|
||||
--buf[size - 1];
|
||||
remainder += divisor;
|
||||
}
|
||||
return digits::done;
|
||||
uint64_t down = diff - unit; // wp_Wdown
|
||||
if (remainder < down && error - remainder >= divisor &&
|
||||
(remainder + divisor < down ||
|
||||
down - remainder > remainder + divisor - down)) {
|
||||
return digits::error;
|
||||
}
|
||||
return 2 * unit <= remainder && remainder <= error - 4 * unit
|
||||
? digits::done
|
||||
: digits::error;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Double, typename std::enable_if<
|
||||
sizeof(Double) == sizeof(uint64_t), int>::type>
|
||||
FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
|
||||
bool fixed, int& exp) {
|
||||
FMT_FUNC bool grisu_format(Double value, buffer<char>& buf, int precision,
|
||||
unsigned options, int& exp) {
|
||||
FMT_ASSERT(value >= 0, "value is negative");
|
||||
bool fixed = (options & grisu_options::fixed) != 0;
|
||||
if (value <= 0) { // <= instead of == to silence a warning.
|
||||
if (precision < 0 || !fixed) {
|
||||
exp = 0;
|
||||
@@ -685,7 +710,7 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
|
||||
min_exp - (fp_value.e + fp::significand_size), cached_exp10);
|
||||
fp_value = fp_value * cached_pow;
|
||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||
if (grisu2_gen_digits(fp_value, 1, exp, handler) == digits::error)
|
||||
if (grisu_gen_digits(fp_value, 1, exp, handler) == digits::error)
|
||||
return false;
|
||||
buf.resize(to_unsigned(handler.size));
|
||||
} else {
|
||||
@@ -695,17 +720,29 @@ FMT_FUNC bool grisu2_format(Double value, buffer<char>& buf, int precision,
|
||||
// the exponent in the range [min_exp, -32].
|
||||
auto cached_pow = get_cached_power( // \tilde{c}_{-k} in Grisu.
|
||||
min_exp - (upper.e + fp::significand_size), cached_exp10);
|
||||
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||
assert(min_exp <= upper.e && upper.e <= -32);
|
||||
fp_value.normalize();
|
||||
fp_value = fp_value * cached_pow;
|
||||
lower = lower * cached_pow; // \tilde{M}^- in Grisu.
|
||||
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||
shortest_handler handler{buf.data(), 0, upper - fp_value};
|
||||
auto result = grisu2_gen_digits(upper, upper.f - lower.f, exp, handler);
|
||||
upper = upper * cached_pow; // \tilde{M}^+ in Grisu.
|
||||
assert(min_exp <= upper.e && upper.e <= -32);
|
||||
auto result = digits::result();
|
||||
int size = 0;
|
||||
if ((options & grisu_options::grisu3) != 0) {
|
||||
--lower.f; // \tilde{M}^- - 1 ulp -> M^-_{\downarrow}.
|
||||
++upper.f; // \tilde{M}^+ + 1 ulp -> M^+_{\uparrow}.
|
||||
// Numbers outside of (lower, upper) definitely do not round to value.
|
||||
grisu_shortest_handler<3> handler{buf.data(), 0, (upper - fp_value).f};
|
||||
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
|
||||
size = handler.size;
|
||||
} else {
|
||||
++lower.f; // \tilde{M}^- + 1 ulp -> M^-_{\uparrow}.
|
||||
--upper.f; // \tilde{M}^+ - 1 ulp -> M^+_{\downarrow}.
|
||||
grisu_shortest_handler<2> handler{buf.data(), 0, (upper - fp_value).f};
|
||||
result = grisu_gen_digits(upper, upper.f - lower.f, exp, handler);
|
||||
size = handler.size;
|
||||
}
|
||||
if (result == digits::error) return false;
|
||||
buf.resize(to_unsigned(handler.size));
|
||||
buf.resize(to_unsigned(size));
|
||||
}
|
||||
exp -= cached_exp10;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user