Refactor floating-point formatting
This commit is contained in:
parent
9108b25da9
commit
7395472dde
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user