diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 50760cab..b4147b41 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -515,8 +515,9 @@ int grisu2_gen_digits(char* buf, fp value, uint64_t error_ulp, int& exp, uint64_t remainder = (static_cast(integral) << -one.e) + fractional; if (stop(buf, size, remainder, data::POWERS_OF_10_64[exp] << -one.e, - error_ulp, exp, true)) + error_ulp, exp, true)) { return size; + } } while (exp > 0); // Generate digits for the fractional part. for (;;) { @@ -542,8 +543,9 @@ struct fixed_stop { if (exp >= 0) precision += exp; } - bool operator()(char*, int& size, uint64_t remainder, uint64_t divisor, - uint64_t error, int&, bool integral) { + // TODO: test + bool operator()(char* buf, int& size, uint64_t remainder, uint64_t divisor, + uint64_t error, int& exp, bool integral) { assert(remainder < divisor); if (size != precision) return false; if (!integral) { @@ -560,7 +562,20 @@ struct fixed_stop { // Round down if (remainder + error) * 2 <= divisor. if (remainder < divisor - remainder && error * 2 <= divisor - remainder * 2) return true; - // TODO: round up + // Round up if (remainder - error) * 2 >= divisor. + if (remainder >= error && + remainder - error >= divisor - (remainder - error)) { + ++buf[size - 1]; + for (int i = size - 1; i > 0 && buf[i] > '9'; --i) { + buf[i] = '0'; + ++buf[i - 1]; + } + if (buf[0] > '9') { + buf[0] = '1'; + ++exp; + } + return true; + } size = -1; return true; } diff --git a/include/fmt/format.h b/include/fmt/format.h index 07f84c6e..9c935038 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -1178,10 +1178,10 @@ template It write_exponent(int exp, It it) { // The number is given as v = digits * pow(10, exp). template -It grisu2_prettify(const char* digits, int size, int exp, It it) { +It grisu2_prettify(const char* digits, int size, int exp, It it, + gen_digits_params params) { // pow(10, full_exp - 1) <= v <= pow(10, full_exp). int full_exp = size + exp; - auto params = gen_digits_params(); params.fixed = (full_exp - 1) >= -4 && (full_exp - 1) <= 10; if (!params.fixed) { // Insert a decimal point after the first digit and add an exponent. @@ -1194,7 +1194,6 @@ It grisu2_prettify(const char* digits, int size, int exp, It it) { *it++ = static_cast(params.upper ? 'E' : 'e'); return write_exponent(exp, it); } - params.trailing_zeros = true; const int exp_threshold = 21; if (size <= full_exp && full_exp <= exp_threshold) { // 1234e7 -> 12340000000[.0+] @@ -2682,13 +2681,16 @@ template class basic_writer { size_t size_; char sign_; int exp_; + internal::gen_digits_params params_; public: - grisu_writer(char sign, internal::buffer& digits, int exp) - : digits_(digits), sign_(sign), exp_(exp) { + grisu_writer(char sign, internal::buffer& digits, int exp, + const internal::gen_digits_params& params) + : digits_(digits), sign_(sign), exp_(exp), params_(params) { int num_digits = static_cast(digits.size()); auto it = internal::grisu2_prettify( - digits.data(), num_digits, exp, internal::counting_iterator()); + digits.data(), num_digits, exp, internal::counting_iterator(), + params_); size_ = it.count(); } @@ -2699,7 +2701,7 @@ template class basic_writer { if (sign_) *it++ = static_cast(sign_); int num_digits = static_cast(digits_.size()); it = internal::grisu2_prettify(digits_.data(), num_digits, - exp_, it); + exp_, it, params_); } }; @@ -2855,8 +2857,7 @@ void basic_writer::write_double(T value, const format_specs& spec) { internal::handle_float_type_spec(handler.type, handler); char sign = 0; - // Use signbit instead of value < 0 because the latter is always - // false for NaN. + // Use signbit instead of value < 0 since the latter is always false for NaN. if (std::signbit(value)) { sign = '-'; value = -value; @@ -2874,21 +2875,20 @@ void basic_writer::write_double(T value, const format_specs& spec) { } } write_inf_or_nan = {*this, spec, sign, handler.as_percentage}; - // Format NaN and ininity ourselves because sprintf's output is not consistent + // Format ininity and NaN ourselves because sprintf's output is not consistent // across platforms. - if (internal::fputil::isnotanumber(value)) - return write_inf_or_nan(handler.upper ? "NAN" : "nan"); if (internal::fputil::isinfinity(value)) return write_inf_or_nan(handler.upper ? "INF" : "inf"); + if (internal::fputil::isnotanumber(value)) + return write_inf_or_nan(handler.upper ? "NAN" : "nan"); + + if (handler.as_percentage) value *= 100; memory_buffer buffer; int exp = 0; int precision = spec.has_precision() || !spec.type ? spec.precision : 6; - - if (handler.as_percentage) value *= 100.; - bool use_grisu = fmt::internal::use_grisu() && - !spec.type && + (!spec.type || handler.fixed) && internal::grisu2_format(static_cast(value), buffer, precision, handler.fixed, exp); if (!use_grisu) internal::sprintf_format(value, buffer, spec); @@ -2909,10 +2909,13 @@ void basic_writer::write_double(T value, const format_specs& spec) { } else if (spec.align() == ALIGN_DEFAULT) { as.align_ = ALIGN_RIGHT; } - if (use_grisu) - write_padded(as, grisu_writer{sign, buffer, exp}); - else + if (use_grisu) { + auto params = internal::gen_digits_params(); + if (precision != 0) params.trailing_zeros = true; + write_padded(as, grisu_writer{sign, buffer, exp, params}); + } else { write_padded(as, double_writer{sign, buffer}); + } } // Reports a system error without throwing an exception.