Refactor floating-point formatting

This commit is contained in:
Victor Zverovich 2019-11-22 11:15:09 -08:00
parent 9108b25da9
commit 7395472dde
4 changed files with 26 additions and 32 deletions

View File

@ -1029,12 +1029,12 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
} }
} }
template <typename Double, template <typename Float, enable_if_t<(sizeof(Float) == sizeof(uint64_t)), int>>
enable_if_t<(sizeof(Double) == sizeof(uint64_t)), int>> bool grisu_format(Float value, int precision, buffer<char>& buf,
bool grisu_format(Double value, buffer<char>& buf, int precision, float_spec spec, int& exp) {
unsigned options, int& exp) {
FMT_ASSERT(value >= 0, "value is negative"); FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = (options & grisu_options::fixed) != 0;
const bool fixed = spec.format == float_format::fixed;
if (value <= 0) { // <= instead of == to silence a warning. if (value <= 0) { // <= instead of == to silence a warning.
if (precision <= 0 || !fixed) { if (precision <= 0 || !fixed) {
exp = 0; exp = 0;
@ -1071,7 +1071,7 @@ bool grisu_format(Double value, buffer<char>& buf, int precision,
} else { } else {
fp fp_value; fp fp_value;
fp lower, upper; // w^- and w^+ in the Grisu paper. fp lower, upper; // w^- and w^+ in the Grisu paper.
if ((options & grisu_options::binary32) != 0) if (spec.binary32)
fp_value.assign_float_with_boundaries(value, lower, upper); fp_value.assign_float_with_boundaries(value, lower, upper);
else else
fp_value.assign_with_boundaries(value, lower, upper); fp_value.assign_with_boundaries(value, lower, upper);

View File

@ -1075,6 +1075,7 @@ struct float_spec {
bool locale; bool locale;
bool percent; bool percent;
bool alt; bool alt;
bool binary32;
}; };
struct gen_digits_params { struct gen_digits_params {
@ -1116,7 +1117,7 @@ template <typename Char> class float_writer {
gen_digits_params params_; gen_digits_params params_;
Char decimal_point_; Char decimal_point_;
template <typename It> It grisu_prettify(It it) const { template <typename It> It prettify(It it) const {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp). // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int full_exp = num_digits_ + exp_; int full_exp = num_digits_ + exp_;
if (params_.format == float_format::exp) { if (params_.format == float_format::exp) {
@ -1194,7 +1195,7 @@ template <typename Char> class float_writer {
? float_format::fixed ? float_format::fixed
: float_format::exp; : float_format::exp;
} }
size_ = grisu_prettify(counting_iterator()).count(); size_ = prettify(counting_iterator()).count();
size_ += params_.sign ? 1 : 0; size_ += params_.sign ? 1 : 0;
} }
@ -1203,20 +1204,16 @@ template <typename Char> class float_writer {
template <typename It> void operator()(It&& it) { template <typename It> void operator()(It&& it) {
if (params_.sign) *it++ = static_cast<Char>(data::signs[params_.sign]); if (params_.sign) *it++ = static_cast<Char>(data::signs[params_.sign]);
it = grisu_prettify(it); it = prettify(it);
} }
}; };
namespace grisu_options {
enum { fixed = 1, binary32 = 2 };
}
// Formats value using the Grisu algorithm: // Formats value using the Grisu algorithm:
// https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf // https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))> template <typename Float, FMT_ENABLE_IF(sizeof(Float) == sizeof(uint64_t))>
FMT_API bool grisu_format(Double, buffer<char>&, int, unsigned, int&); FMT_API bool grisu_format(Float, int, buffer<char>&, float_spec, int&);
template <typename Double, FMT_ENABLE_IF(sizeof(Double) != sizeof(uint64_t))> template <typename Float, FMT_ENABLE_IF(sizeof(Float) != sizeof(uint64_t))>
inline bool grisu_format(Double, buffer<char>&, int, unsigned, int&) { inline bool grisu_format(Float, int, buffer<char>&, float_spec, int&) {
return false; return false;
} }
@ -1739,17 +1736,13 @@ template <typename Range> class basic_writer {
return; return;
} }
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
int num_digits = if (fspec.format == float_format::exp) ++precision;
fspec.format == float_format::exp ? precision + 1 : precision; if (const_check(std::is_same<T, float>())) fspec.binary32 = true;
unsigned options = 0;
if (fspec.format == float_format::fixed) options |= grisu_options::fixed;
if (const_check(std::is_same<T, float>()))
options |= grisu_options::binary32;
if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) value *= 100; if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) value *= 100;
int exp = 0; int exp = 0;
bool use_grisu = internal::use_grisu<T>() && bool use_grisu =
grisu_format(static_cast<double>(value), buffer, internal::use_grisu<T>() &&
num_digits, options, exp); grisu_format(static_cast<double>(value), precision, buffer, fspec, exp);
if (!use_grisu) exp = sprintf_format(value, precision, fspec, buffer); if (!use_grisu) exp = sprintf_format(value, precision, fspec, buffer);
if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) { if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) {
@ -1759,18 +1752,18 @@ template <typename Range> class basic_writer {
auto params = gen_digits_params(); auto params = gen_digits_params();
params.sign = sign; params.sign = sign;
params.format = fspec.format; params.format = fspec.format;
params.num_digits = num_digits; params.num_digits = precision;
params.trailing_zeros = params.trailing_zeros =
(precision != 0 && (precision != 0 &&
(!specs.type || fspec.format == float_format::fixed || (!specs.type || fspec.format == float_format::fixed ||
fspec.format == float_format::exp)) || fspec.format == float_format::exp)) ||
specs.alt; specs.alt;
params.upper = fspec.upper; params.upper = fspec.upper;
num_digits = static_cast<int>(buffer.size());
char_type point = fspec.locale ? decimal_point<char_type>(locale_) char_type point = fspec.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.'); : static_cast<char_type>('.');
write_padded(specs, float_writer<char_type>(buffer.data(), num_digits, exp, write_padded(specs, float_writer<char_type>(buffer.data(),
params, point)); static_cast<int>(buffer.size()),
exp, params, point));
} }
void write(char value) { void write(char value) {

View File

@ -11,7 +11,8 @@ FMT_BEGIN_NAMESPACE
template struct FMT_API internal::basic_data<void>; template struct FMT_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of grisu_format. // Workaround a bug in MSVC2013 that prevents instantiation of grisu_format.
bool (*instantiate_grisu_format)(double, internal::buffer<char>&, int, unsigned, bool (*instantiate_grisu_format)(double, int, internal::buffer<char>&,
internal::float_spec,
int&) = internal::grisu_format; int&) = internal::grisu_format;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR

View File

@ -327,7 +327,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) { TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf; fmt::memory_buffer buf;
int exp = 0; int exp = 0;
grisu_format(4.2f, buf, -1, false, exp); grisu_format(4.2f, -1, buf, fmt::internal::float_spec(), exp);
} }
template <typename T> struct value_extractor { template <typename T> struct value_extractor {