float_spec -> float_specs

This commit is contained in:
Victor Zverovich 2019-11-26 15:52:56 -08:00
parent 9a21728b0a
commit c68703c9f4
4 changed files with 74 additions and 69 deletions

View File

@ -1039,11 +1039,11 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
// (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf) // (https://www.cs.tufts.edu/~nr/cs257/archive/florian-loitsch/printf.pdf)
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise. // if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T> template <typename T>
int format_float(T value, int precision, float_spec spec, buffer<char>& buf) { int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
static_assert(!std::is_same<T, float>(), ""); static_assert(!std::is_same<T, float>(), "");
FMT_ASSERT(value >= 0, "value is negative"); FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = spec.format == float_format::fixed; const bool fixed = specs.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) {
buf.push_back('0'); buf.push_back('0');
@ -1054,20 +1054,20 @@ int format_float(T value, int precision, float_spec spec, buffer<char>& buf) {
return -precision; return -precision;
} }
if (!spec.use_grisu) return snprintf_float(value, precision, spec, buf); if (!specs.use_grisu) return snprintf_float(value, precision, specs, buf);
int exp = 0; int exp = 0;
const int min_exp = -60; // alpha in Grisu. const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu. int cached_exp10 = 0; // K in Grisu.
if (precision != -1) { if (precision != -1) {
if (precision > 17) return snprintf_float(value, precision, spec, buf); if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value)); fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power( const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10); min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow; normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed}; fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error) if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, spec, buf); return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size; int num_digits = handler.size;
if (!fixed) { if (!fixed) {
// Remove trailing zeros. // Remove trailing zeros.
@ -1079,7 +1079,7 @@ int format_float(T value, int precision, float_spec spec, buffer<char>& buf) {
buf.resize(to_unsigned(num_digits)); buf.resize(to_unsigned(num_digits));
} else { } else {
fp fp_value; fp fp_value;
auto boundaries = spec.binary32 auto boundaries = specs.binary32
? fp_value.assign_float_with_boundaries(value) ? fp_value.assign_float_with_boundaries(value)
: fp_value.assign_with_boundaries(value); : fp_value.assign_with_boundaries(value);
fp_value = normalize(fp_value); fp_value = normalize(fp_value);
@ -1111,14 +1111,16 @@ int format_float(T value, int precision, float_spec spec, buffer<char>& buf) {
} }
template <typename T> template <typename T>
int snprintf_float(T value, int precision, float_spec spec, buffer<char>& buf) { int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail. // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer"); FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
static_assert(!std::is_same<T, float>(), ""); static_assert(!std::is_same<T, float>(), "");
// Subtract 1 to account for the difference in precision since we use %e for // Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format. // both general and exponent format.
if (spec.format == float_format::general || spec.format == float_format::exp) if (specs.format == float_format::general ||
specs.format == float_format::exp)
precision = (precision >= 0 ? precision : 6) - 1; precision = (precision >= 0 ? precision : 6) - 1;
// Build the format string. // Build the format string.
@ -1126,15 +1128,15 @@ int snprintf_float(T value, int precision, float_spec spec, buffer<char>& buf) {
char format[max_format_size]; char format[max_format_size];
char* format_ptr = format; char* format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
if (spec.alt) *format_ptr++ = '#'; if (specs.alt) *format_ptr++ = '#';
if (precision >= 0) { if (precision >= 0) {
*format_ptr++ = '.'; *format_ptr++ = '.';
*format_ptr++ = '*'; *format_ptr++ = '*';
} }
if (std::is_same<T, long double>()) *format_ptr++ = 'L'; if (std::is_same<T, long double>()) *format_ptr++ = 'L';
*format_ptr++ = spec.format != float_format::hex *format_ptr++ = specs.format != float_format::hex
? (spec.format == float_format::fixed ? 'f' : 'e') ? (specs.format == float_format::fixed ? 'f' : 'e')
: (spec.upper ? 'A' : 'a'); : (specs.upper ? 'A' : 'a');
*format_ptr = '\0'; *format_ptr = '\0';
// Format using snprintf. // Format using snprintf.
@ -1163,7 +1165,7 @@ int snprintf_float(T value, int precision, float_spec spec, buffer<char>& buf) {
continue; continue;
} }
auto is_digit = [](char c) { return c >= '0' && c <= '9'; }; auto is_digit = [](char c) { return c >= '0' && c <= '9'; };
if (spec.format == float_format::fixed) { if (specs.format == float_format::fixed) {
if (precision == 0) { if (precision == 0) {
buf.resize(size); buf.resize(size);
return 0; return 0;
@ -1178,7 +1180,7 @@ int snprintf_float(T value, int precision, float_spec spec, buffer<char>& buf) {
buf.resize(size - 1); buf.resize(size - 1);
return -fraction_size; return -fraction_size;
} }
if (spec.format == float_format::hex) { if (specs.format == float_format::hex) {
buf.resize(size + offset); buf.resize(size + offset);
return 0; return 0;
} }

View File

@ -1062,24 +1062,24 @@ using format_specs = basic_format_specs<char>;
namespace internal { namespace internal {
// A floating-point presentation format. // A floating-point presentation format.
enum class float_format { enum class float_format : unsigned char {
general, // General: exponent notation or fixed point based on magnitude. general, // General: exponent notation or fixed point based on magnitude.
exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3.
fixed, // Fixed point with the default precision of 6, e.g. 0.0012. fixed, // Fixed point with the default precision of 6, e.g. 0.0012.
hex hex
}; };
struct float_spec { struct float_specs {
int precision; int precision;
float_format format; float_format format : 8;
sign_t sign : 3; sign_t sign : 8;
bool upper; bool upper : 1;
bool locale; bool locale : 1;
bool percent; bool percent : 1;
bool alt; bool alt : 1;
bool binary32; bool binary32 : 1;
bool use_grisu; bool use_grisu : 1;
bool trailing_zeros; bool trailing_zeros : 1;
}; };
// Writes the exponent exp in the form "[+-]d{2,3}" to buffer. // Writes the exponent exp in the form "[+-]d{2,3}" to buffer.
@ -1110,7 +1110,7 @@ template <typename Char> class float_writer {
int num_digits_; int num_digits_;
int exp_; int exp_;
size_t size_; size_t size_;
float_spec params_; float_specs params_;
Char decimal_point_; Char decimal_point_;
template <typename It> It prettify(It it) const { template <typename It> It prettify(It it) const {
@ -1182,21 +1182,21 @@ template <typename Char> class float_writer {
} }
public: public:
float_writer(const char* digits, int num_digits, int exp, float_spec spec, float_writer(const char* digits, int num_digits, int exp, float_specs specs,
Char decimal_point) Char decimal_point)
: digits_(digits), : digits_(digits),
num_digits_(num_digits), num_digits_(num_digits),
exp_(exp), exp_(exp),
params_(spec), params_(specs),
decimal_point_(decimal_point) { decimal_point_(decimal_point) {
int full_exp = num_digits + exp - 1; int full_exp = num_digits + exp - 1;
int precision = spec.precision > 0 ? spec.precision : 16; int precision = specs.precision > 0 ? specs.precision : 16;
if (params_.format == float_format::general && if (params_.format == float_format::general &&
!(full_exp >= -4 && full_exp < precision)) { !(full_exp >= -4 && full_exp < precision)) {
params_.format = float_format::exp; params_.format = float_format::exp;
} }
size_ = prettify(counting_iterator()).count(); size_ = prettify(counting_iterator()).count();
size_ += spec.sign ? 1 : 0; size_ += specs.sign ? 1 : 0;
} }
size_t size() const { return size_; } size_t size() const { return size_; }
@ -1209,11 +1209,12 @@ template <typename Char> class float_writer {
}; };
template <typename T> template <typename T>
int format_float(T value, int precision, float_spec spec, buffer<char>& buf); int format_float(T value, int precision, float_specs specs, buffer<char>& buf);
// Formats a floating-point number with snprintf. // Formats a floating-point number with snprintf.
template <typename T> template <typename T>
int snprintf_float(T value, int precision, float_spec spec, buffer<char>& buf); int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf);
template <typename T> T promote_float(T value) { return value; } template <typename T> T promote_float(T value) { return value; }
inline double promote_float(float value) { return value; } inline double promote_float(float value) { return value; }
@ -1245,9 +1246,9 @@ FMT_CONSTEXPR void handle_int_type_spec(char spec, Handler&& handler) {
} }
template <typename ErrorHandler = error_handler> template <typename ErrorHandler = error_handler>
FMT_CONSTEXPR float_spec parse_float_type_spec(char spec, FMT_CONSTEXPR float_specs parse_float_type_spec(char spec,
ErrorHandler&& eh = {}) { ErrorHandler&& eh = {}) {
auto result = float_spec(); auto result = float_specs();
switch (spec) { switch (spec) {
case 'G': case 'G':
result.upper = true; result.upper = true;
@ -1690,62 +1691,63 @@ template <typename Range> class basic_writer {
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
void write(T value, format_specs specs = {}) { void write(T value, format_specs specs = {}) {
float_spec fspec = parse_float_type_spec(specs.type); float_specs fspecs = parse_float_type_spec(specs.type);
fspec.sign = specs.sign; fspecs.sign = specs.sign;
if (std::signbit(value)) { // value < 0 is false for NaN so use signbit. if (std::signbit(value)) { // value < 0 is false for NaN so use signbit.
fspec.sign = sign::minus; fspecs.sign = sign::minus;
value = -value; value = -value;
} else if (fspec.sign == sign::minus) { } else if (fspecs.sign == sign::minus) {
fspec.sign = sign::none; fspecs.sign = sign::none;
} }
if (!std::isfinite(value)) { if (!std::isfinite(value)) {
auto str = std::isinf(value) ? (fspec.upper ? "INF" : "inf") auto str = std::isinf(value) ? (fspecs.upper ? "INF" : "inf")
: (fspec.upper ? "NAN" : "nan"); : (fspecs.upper ? "NAN" : "nan");
return write_padded(specs, inf_or_nan_writer<char_type>{fspec.sign, str}); return write_padded(specs,
inf_or_nan_writer<char_type>{fspecs.sign, str});
} }
if (specs.align == align::none) { if (specs.align == align::none) {
specs.align = align::right; specs.align = align::right;
} else if (specs.align == align::numeric) { } else if (specs.align == align::numeric) {
if (fspec.sign) { if (fspecs.sign) {
auto&& it = reserve(1); auto&& it = reserve(1);
*it++ = static_cast<char_type>(data::signs[fspec.sign]); *it++ = static_cast<char_type>(data::signs[fspecs.sign]);
fspec.sign = sign::none; fspecs.sign = sign::none;
if (specs.width != 0) --specs.width; if (specs.width != 0) --specs.width;
} }
specs.align = align::right; specs.align = align::right;
} }
memory_buffer buffer; memory_buffer buffer;
if (fspec.format == float_format::hex) { if (fspecs.format == float_format::hex) {
if (fspec.sign) buffer.push_back(data::signs[fspec.sign]); if (fspecs.sign) buffer.push_back(data::signs[fspecs.sign]);
fspec.alt = specs.alt; fspecs.alt = specs.alt;
snprintf_float(promote_float(value), specs.precision, fspec, buffer); snprintf_float(promote_float(value), specs.precision, fspecs, buffer);
write_padded(specs, str_writer<char>{buffer.data(), buffer.size()}); write_padded(specs, str_writer<char>{buffer.data(), buffer.size()});
return; return;
} }
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6; int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
if (fspec.format == float_format::exp) ++precision; if (fspecs.format == float_format::exp) ++precision;
fspec.trailing_zeros = fspecs.trailing_zeros =
(precision != 0 && (precision != 0 &&
(!specs.type || fspec.format == float_format::fixed || (!specs.type || fspecs.format == float_format::fixed ||
fspec.format == float_format::exp)) || fspecs.format == float_format::exp)) ||
specs.alt; specs.alt;
if (const_check(std::is_same<T, float>())) fspec.binary32 = true; if (const_check(std::is_same<T, float>())) fspecs.binary32 = true;
fspec.use_grisu = use_grisu<T>(); fspecs.use_grisu = use_grisu<T>();
if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) value *= 100; if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) value *= 100;
int exp = format_float(promote_float(value), precision, fspec, buffer); int exp = format_float(promote_float(value), precision, fspecs, buffer);
if (const_check(FMT_DEPRECATED_PERCENT) && fspec.percent) { if (const_check(FMT_DEPRECATED_PERCENT) && fspecs.percent) {
buffer.push_back('%'); buffer.push_back('%');
--exp; // Adjust decimal place position. --exp; // Adjust decimal place position.
} }
fspec.precision = precision; fspecs.precision = precision;
char_type point = fspec.locale ? decimal_point<char_type>(locale_) char_type point = fspecs.locale ? decimal_point<char_type>(locale_)
: static_cast<char_type>('.'); : static_cast<char_type>('.');
write_padded(specs, float_writer<char_type>(buffer.data(), write_padded(specs, float_writer<char_type>(buffer.data(),
static_cast<int>(buffer.size()), static_cast<int>(buffer.size()),
exp, fspec, point)); exp, fspecs, point));
} }
void write(char value) { void write(char value) {

View File

@ -11,7 +11,7 @@ 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 format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_spec, int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) = internal::buffer<char>&) =
internal::format_float; internal::format_float;
@ -37,15 +37,16 @@ template FMT_API std::string internal::vformat<char>(
template FMT_API format_context::iterator internal::vformat_to( template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>); internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int, internal::float_spec, template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&); internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int, template FMT_API int internal::snprintf_float(long double, int,
internal::float_spec, internal::float_specs,
internal::buffer<char>&); internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_spec, template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&); internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int, template FMT_API int internal::format_float(long double, int,
internal::float_spec, internal::float_specs,
internal::buffer<char>&); internal::buffer<char>&);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.

View File

@ -318,7 +318,7 @@ TEST(FPTest, FixedHandler) {
TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) { TEST(FPTest, GrisuFormatCompilesWithNonIEEEDouble) {
fmt::memory_buffer buf; fmt::memory_buffer buf;
format_float(0.42, -1, fmt::internal::float_spec(), buf); format_float(0.42, -1, fmt::internal::float_specs(), buf);
} }
template <typename T> struct value_extractor { template <typename T> struct value_extractor {