Don't emit more than precision digits (#1072)

This commit is contained in:
Victor Zverovich 2019-03-09 13:53:23 -08:00
parent 3466d9c845
commit 49d244c065
3 changed files with 16 additions and 6 deletions

View File

@ -459,8 +459,8 @@ int grisu2_gen_digits(char* buf, fp value, uint64_t error_ulp, int& exp,
// The fractional part of scaled value (p2 in Grisu) c = value % one. // The fractional part of scaled value (p2 in Grisu) c = value % one.
uint64_t fractional = value.f & (one.f - 1); uint64_t fractional = value.f & (one.f - 1);
exp = count_digits(integral); // kappa in Grisu. exp = count_digits(integral); // kappa in Grisu.
stop.on_exp(exp);
int size = 0; int size = 0;
if (stop.on_exp(exp)) return size;
// Generate digits for the integral part. This can produce up to 10 digits. // Generate digits for the integral part. This can produce up to 10 digits.
do { do {
uint32_t digit = 0; uint32_t digit = 0;
@ -537,17 +537,23 @@ struct fixed_stop {
int exp10; int exp10;
bool fixed; bool fixed;
void on_exp(int exp) { bool enough_precision(int full_exp) const {
if (!fixed) return; return full_exp <= 0 && -full_exp >= precision;
}
bool on_exp(int exp) {
if (!fixed) return false;
exp += exp10; exp += exp10;
if (exp >= 0) precision += exp; if (exp >= 0) precision += exp;
// TODO: round
return enough_precision(exp);
} }
// TODO: test // TODO: test
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor, bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
uint64_t error, int& exp, bool integral) { uint64_t error, int& exp, bool integral) {
assert(remainder < divisor); assert(remainder < divisor);
if (size != precision) return false; if (size != precision && !enough_precision(exp + exp10)) return false;
if (!integral) { if (!integral) {
// Check if error * 2 < divisor with overflow prevention. // Check if error * 2 < divisor with overflow prevention.
// The check is not needed for the integral part because error = 1 // The check is not needed for the integral part because error = 1
@ -585,7 +591,7 @@ struct fixed_stop {
struct shortest_stop { struct shortest_stop {
fp diff; // wp_w in Grisu. fp diff; // wp_w in Grisu.
void on_exp(int) {} bool on_exp(int) { return false; }
bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor, bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor,
uint64_t error, int& exp, bool integral) { uint64_t error, int& exp, bool integral) {

View File

@ -2892,7 +2892,7 @@ void basic_writer<Range>::write_double(T value, const format_specs& spec) {
int exp = 0; int exp = 0;
int precision = spec.has_precision() || !spec.type ? spec.precision : 6; int precision = spec.has_precision() || !spec.type ? spec.precision : 6;
bool use_grisu = fmt::internal::use_grisu<T>() && bool use_grisu = fmt::internal::use_grisu<T>() &&
!spec.type && (!spec.type || handler.fixed) &&
internal::grisu2_format(static_cast<double>(value), buffer, internal::grisu2_format(static_cast<double>(value), buffer,
precision, handler.fixed, exp); precision, handler.fixed, exp);
if (!use_grisu) internal::sprintf_format(value, buffer, spec); if (!use_grisu) internal::sprintf_format(value, buffer, spec);

View File

@ -1470,6 +1470,10 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ(buffer, format("{:A}", -42.0)); EXPECT_EQ(buffer, format("{:A}", -42.0));
} }
TEST(FormatterTest, PrecisionRounding) {
EXPECT_EQ("0.000", format("{:.3f}", 0.00049));
}
TEST(FormatterTest, FormatNaN) { TEST(FormatterTest, FormatNaN) {
double nan = std::numeric_limits<double>::quiet_NaN(); double nan = std::numeric_limits<double>::quiet_NaN();
EXPECT_EQ("nan", format("{}", nan)); EXPECT_EQ("nan", format("{}", nan));