forked from fmtlib/fmt
Implement fallback FP formatting with given precision (#1526)
This commit is contained in:
@ -984,7 +984,7 @@ struct grisu_shortest_handler {
|
|||||||
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
// Floating-Point Printout ((FPP)^2) algorithm by Steele & White:
|
||||||
// https://fmt.dev/p372-steele.pdf.
|
// https://fmt.dev/p372-steele.pdf.
|
||||||
template <typename Double>
|
template <typename Double>
|
||||||
void fallback_format(Double d, buffer<char>& buf, int& exp10) {
|
void fallback_format(Double d, int num_digits, buffer<char>& buf, int& exp10) {
|
||||||
bigint numerator; // 2 * R in (FPP)^2.
|
bigint numerator; // 2 * R in (FPP)^2.
|
||||||
bigint denominator; // 2 * S in (FPP)^2.
|
bigint denominator; // 2 * S in (FPP)^2.
|
||||||
// lower and upper are differences between value and corresponding boundaries.
|
// lower and upper are differences between value and corresponding boundaries.
|
||||||
@ -1031,34 +1031,71 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
|
|||||||
upper = &upper_store;
|
upper = &upper_store;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!upper) upper = &lower;
|
|
||||||
// Invariant: value == (numerator / denominator) * pow(10, exp10).
|
// Invariant: value == (numerator / denominator) * pow(10, exp10).
|
||||||
bool even = (value.f & 1) == 0;
|
if (num_digits < 0) {
|
||||||
int num_digits = 0;
|
// Generate the shortest representation.
|
||||||
char* data = buf.data();
|
if (!upper) upper = &lower;
|
||||||
for (;;) {
|
bool even = (value.f & 1) == 0;
|
||||||
int digit = numerator.divmod_assign(denominator);
|
num_digits = 0;
|
||||||
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
char* data = buf.data();
|
||||||
// numerator + upper >[=] pow10:
|
for (;;) {
|
||||||
bool high = add_compare(numerator, *upper, denominator) + even > 0;
|
int digit = numerator.divmod_assign(denominator);
|
||||||
data[num_digits++] = static_cast<char>('0' + digit);
|
bool low = compare(numerator, lower) - even < 0; // numerator <[=] lower.
|
||||||
if (low || high) {
|
// numerator + upper >[=] pow10:
|
||||||
if (!low) {
|
bool high = add_compare(numerator, *upper, denominator) + even > 0;
|
||||||
++data[num_digits - 1];
|
data[num_digits++] = static_cast<char>('0' + digit);
|
||||||
} else if (high) {
|
if (low || high) {
|
||||||
int result = add_compare(numerator, numerator, denominator);
|
if (!low) {
|
||||||
// Round half to even.
|
|
||||||
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
|
||||||
++data[num_digits - 1];
|
++data[num_digits - 1];
|
||||||
|
} else if (high) {
|
||||||
|
int result = add_compare(numerator, numerator, denominator);
|
||||||
|
// Round half to even.
|
||||||
|
if (result > 0 || (result == 0 && (digit % 2) != 0))
|
||||||
|
++data[num_digits - 1];
|
||||||
|
}
|
||||||
|
buf.try_resize(to_unsigned(num_digits));
|
||||||
|
exp10 -= num_digits - 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
numerator *= 10;
|
||||||
|
lower *= 10;
|
||||||
|
if (upper != &lower) *upper *= 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Generate the given number of digits.
|
||||||
|
exp10 -= num_digits - 1;
|
||||||
|
if (num_digits == 0) {
|
||||||
|
buf.try_resize(1);
|
||||||
|
denominator *= 10;
|
||||||
|
buf[0] = add_compare(numerator, numerator, denominator) > 0 ? '1' : '0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buf.try_resize(to_unsigned(num_digits));
|
||||||
|
for (int i = 0; i < num_digits - 1; ++i) {
|
||||||
|
int digit = numerator.divmod_assign(denominator);
|
||||||
|
buf[i] = static_cast<char>('0' + digit);
|
||||||
|
numerator *= 10;
|
||||||
|
}
|
||||||
|
int digit = numerator.divmod_assign(denominator);
|
||||||
|
auto result = add_compare(numerator, numerator, denominator);
|
||||||
|
if (result > 0 || (result == 0 && (digit % 2) != 0)) {
|
||||||
|
if (digit == 9) {
|
||||||
|
const auto overflow = '0' + 10;
|
||||||
|
buf[num_digits - 1] = overflow;
|
||||||
|
// Propagate the carry.
|
||||||
|
for (int i = num_digits - 1; i > 0 && buf[i] == overflow; --i) {
|
||||||
|
buf[i] = '0';
|
||||||
|
++buf[i - 1];
|
||||||
|
}
|
||||||
|
if (buf[0] == overflow) {
|
||||||
|
buf[0] = '0';
|
||||||
|
++exp10;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(num_digits));
|
|
||||||
exp10 -= num_digits - 1;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
numerator *= 10;
|
++digit;
|
||||||
lower *= 10;
|
|
||||||
if (upper != &lower) *upper *= 10;
|
|
||||||
}
|
}
|
||||||
|
buf[num_digits - 1] = static_cast<char>('0' + digit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats value using the Grisu algorithm
|
// Formats value using the Grisu algorithm
|
||||||
@ -1110,7 +1147,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
boundaries.upper - boundaries.lower, exp, handler);
|
boundaries.upper - boundaries.lower, exp, handler);
|
||||||
if (result == digits::error) {
|
if (result == digits::error) {
|
||||||
exp += handler.size - cached_exp10 - 1;
|
exp += handler.size - cached_exp10 - 1;
|
||||||
fallback_format(value, buf, exp);
|
fallback_format(value, -1, buf, exp);
|
||||||
return exp;
|
return exp;
|
||||||
}
|
}
|
||||||
buf.try_resize(to_unsigned(handler.size));
|
buf.try_resize(to_unsigned(handler.size));
|
||||||
@ -1121,8 +1158,11 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
|
|||||||
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
min_exp - (normalized.e + fp::significand_size), cached_exp10);
|
||||||
normalized = normalized * cached_pow;
|
normalized = normalized * cached_pow;
|
||||||
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
|
||||||
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
|
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) {
|
||||||
return snprintf_float(value, precision, specs, buf);
|
exp += handler.size - cached_exp10 - 1;
|
||||||
|
fallback_format(value, handler.size, buf, exp);
|
||||||
|
return exp;
|
||||||
|
}
|
||||||
int num_digits = handler.size;
|
int num_digits = handler.size;
|
||||||
if (!fixed && !specs.showpoint) {
|
if (!fixed && !specs.showpoint) {
|
||||||
// Remove trailing zeros.
|
// Remove trailing zeros.
|
||||||
|
@ -762,8 +762,7 @@ TEST(FormatterTest, HashFlag) {
|
|||||||
EXPECT_EQ("4.e+01", format("{:#.0e}", 42.0));
|
EXPECT_EQ("4.e+01", format("{:#.0e}", 42.0));
|
||||||
EXPECT_EQ("0.", format("{:#.0f}", 0.01));
|
EXPECT_EQ("0.", format("{:#.0f}", 0.01));
|
||||||
EXPECT_EQ("0.50", format("{:#.2g}", 0.5));
|
EXPECT_EQ("0.50", format("{:#.2g}", 0.5));
|
||||||
auto s = format("{:#.0f}", 0.5); // MSVC's printf uses wrong rounding mode.
|
EXPECT_EQ("0.", format("{:#.0f}", 0.5));
|
||||||
EXPECT_TRUE(s == "0." || s == "1.");
|
|
||||||
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
|
EXPECT_THROW_MSG(format("{0:#", 'c'), format_error,
|
||||||
"missing '}' in format string");
|
"missing '}' in format string");
|
||||||
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
|
EXPECT_THROW_MSG(format("{0:#}", 'c'), format_error,
|
||||||
@ -1279,6 +1278,9 @@ TEST(FormatterTest, PrecisionRounding) {
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
safe_sprintf(buffer, "%f", n);
|
safe_sprintf(buffer, "%f", n);
|
||||||
EXPECT_EQ(buffer, format("{:f}", n));
|
EXPECT_EQ(buffer, format("{:f}", n));
|
||||||
|
EXPECT_EQ("225.51575035152063720",
|
||||||
|
fmt::format("{:.17f}", 225.51575035152064));
|
||||||
|
EXPECT_EQ("-761519619559038.2", fmt::format("{:.1f}", -761519619559038.2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatNaN) {
|
TEST(FormatterTest, FormatNaN) {
|
||||||
|
Reference in New Issue
Block a user