diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 96e81dcb..8f2fe1c9 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -534,8 +534,14 @@ int grisu2_gen_digits(char* buf, fp value, uint64_t error_ulp, int& exp, struct fixed_stop { int precision; int exp10; + bool fixed; - void on_exp(int exp) { precision += exp + exp10; } + void on_exp(int exp) { + if (!fixed) return; + exp += exp10; + if (exp >= 0) + precision += exp; + } bool operator()(char*, int& size, uint64_t remainder, uint64_t divisor, uint64_t error, int&, bool integral) { @@ -549,8 +555,9 @@ struct fixed_stop { size = -1; return true; } - } else + } else { assert(error == 1 && divisor > 2); + } // Round down if (remainder + error) * 2 <= divisor. if (remainder < divisor - remainder && error * 2 <= divisor - remainder * 2) return true; @@ -582,7 +589,7 @@ struct shortest_stop { template FMT_FUNC typename std::enable_if::type -grisu2_format(Double value, buffer& buf, int precision, int& exp) { +grisu2_format(Double value, buffer& buf, int precision, bool fixed, int& exp) { FMT_ASSERT(value >= 0, "value is negative"); if (value <= 0) { // <= instead of == to silence a warning. if (precision < 0) { @@ -606,7 +613,7 @@ grisu2_format(Double value, buffer& buf, int precision, int& exp) { min_exp - (fp_value.e + fp::significand_size), cached_exp10); fp_value = fp_value * cached_pow; int size = grisu2_gen_digits(buf.data(), fp_value, 1, exp, - fixed_stop{precision, -cached_exp10}); + fixed_stop{precision, -cached_exp10, fixed}); if (size < 0) return false; buf.resize(to_unsigned(size)); } else { diff --git a/include/fmt/format.h b/include/fmt/format.h index 3139c43d..317597ca 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1139,10 +1139,10 @@ namespace internal { // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf template FMT_API typename std::enable_if::type -grisu2_format(Double value, buffer& buf, int precision, int& exp); +grisu2_format(Double value, buffer& buf, int precision, bool fixed, int& exp); template inline typename std::enable_if::type -grisu2_format(Double, buffer&, int, int&) { +grisu2_format(Double, buffer&, int, bool, int&) { return false; } @@ -2866,9 +2866,9 @@ void basic_writer::write_double(T value, const format_specs& spec) { int exp = 0; int precision = spec.has_precision() || !spec.type ? spec.precision : 6; bool use_grisu = fmt::internal::use_grisu() && - (!spec.type || handler.fixed) && !spec.has_precision() && + (!spec.type || handler.fixed) && internal::grisu2_format(static_cast(value), buffer, - precision, exp); + precision, handler.fixed, exp); if (!use_grisu) internal::sprintf_format(value, buffer, spec); align_spec as = spec; if (spec.align() == ALIGN_NUMERIC) { diff --git a/src/format.cc b/src/format.cc index d054e2b5..585ab6c1 100644 --- a/src/format.cc +++ b/src/format.cc @@ -11,7 +11,7 @@ FMT_BEGIN_NAMESPACE template struct internal::basic_data; // Workaround a bug in MSVC2013 that prevents instantiation of grisu2_format. -bool (*instantiate_grisu2_format)(double, internal::buffer&, int, +bool (*instantiate_grisu2_format)(double, internal::buffer&, int, bool, int&) = internal::grisu2_format; #ifndef FMT_STATIC_THOUSANDS_SEPARATOR diff --git a/test/format-impl-test.cc b/test/format-impl-test.cc index 731501ac..a335b8d6 100644 --- a/test/format-impl-test.cc +++ b/test/format-impl-test.cc @@ -103,7 +103,7 @@ TEST(FPTest, GetCachedPower) { TEST(FPTest, Grisu2FormatCompilesWithNonIEEEDouble) { fmt::memory_buffer buf; int exp = 0; - grisu2_format(4.2f, buf, -1, exp); + grisu2_format(4.2f, buf, -1, false, exp); } template struct ValueExtractor : fmt::internal::function {