Improve handling of signs

This commit is contained in:
Victor Zverovich 2019-10-20 19:02:38 -07:00
parent 8498bc97dd
commit a967dcbe20
2 changed files with 61 additions and 65 deletions

View File

@ -342,6 +342,7 @@ template <typename T>
const char basic_data<T>::background_color[] = "\x1b[48;2;";
template <typename T> const char basic_data<T>::reset_color[] = "\x1b[0m";
template <typename T> const wchar_t basic_data<T>::wreset_color[] = L"\x1b[0m";
template <typename T> const char basic_data<T>::signs[] = {0, '-', '+', ' '};
template <typename T> struct bits {
static FMT_CONSTEXPR_DECL const int value =

View File

@ -670,6 +670,7 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
static const char background_color[];
static const char reset_color[5];
static const wchar_t wreset_color[5];
static const char signs[];
};
FMT_EXTERN template struct basic_data<void>;
@ -1042,6 +1043,7 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
struct gen_digits_params {
int num_digits;
sign_t sign : 3;
bool fixed;
bool upper;
bool trailing_zeros;
@ -1308,6 +1310,41 @@ void arg_map<Context>::init(const basic_format_args<Context>& args) {
}
}
template <typename Char> class grisu_writer {
private:
buffer<char>& digits_;
size_t size_;
int exp_;
gen_digits_params params_;
Char decimal_point_;
public:
grisu_writer(buffer<char>& digits, int exp,
const internal::gen_digits_params& params, Char decimal_point)
: digits_(digits),
exp_(exp),
params_(params),
decimal_point_(decimal_point) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 16;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = grisu_prettify(digits.data(), num_digits, exp,
counting_iterator<char>(), params_, '\0');
size_ = it.count();
}
size_t size() const { return size_ + (params_.sign ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (params_.sign) *it++ = static_cast<Char>(data::signs[params_.sign]);
int num_digits = static_cast<int>(digits_.size());
it = grisu_prettify(digits_.data(), num_digits, exp_, it, params_,
decimal_point_);
}
};
// This template provides operations for formatting and writing data into a
// character range.
template <typename Range> class basic_writer {
@ -1510,7 +1547,7 @@ template <typename Range> class basic_writer {
enum { inf_size = 3 }; // This is an enum to workaround a bug in MSVC.
struct inf_or_nan_writer {
char sign;
sign_t sign;
bool as_percentage;
const char* str;
@ -1521,7 +1558,7 @@ template <typename Range> class basic_writer {
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) const {
if (sign) *it++ = static_cast<char_type>(sign);
if (sign) *it++ = static_cast<char_type>(data::signs[sign]);
it = internal::copy_str<char_type>(
str, str + static_cast<std::size_t>(inf_size), it);
if (as_percentage) *it++ = static_cast<char_type>('%');
@ -1529,7 +1566,7 @@ template <typename Range> class basic_writer {
};
struct double_writer {
char sign;
sign_t sign;
internal::buffer<char>& buffer;
char* decimal_point_pos;
char_type decimal_point;
@ -1538,7 +1575,7 @@ template <typename Range> class basic_writer {
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign) *it++ = static_cast<char_type>(sign);
if (sign) *it++ = static_cast<char_type>(data::signs[sign]);
auto begin = buffer.begin();
if (decimal_point_pos) {
it = internal::copy_str<char_type>(begin, decimal_point_pos, it);
@ -1549,45 +1586,6 @@ template <typename Range> class basic_writer {
}
};
class grisu_writer {
private:
internal::buffer<char>& digits_;
size_t size_;
char sign_;
int exp_;
internal::gen_digits_params params_;
char_type decimal_point_;
public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params,
char_type decimal_point)
: digits_(digits),
sign_(sign),
exp_(exp),
params_(params),
decimal_point_(decimal_point) {
int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 16;
params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_, '.');
size_ = it.count();
}
size_t size() const { return size_ + (sign_ ? 1 : 0); }
size_t width() const { return size(); }
template <typename It> void operator()(It&& it) {
if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_, decimal_point_);
}
};
template <typename Char> struct str_writer {
const Char* s;
size_t size_;
@ -2780,18 +2778,15 @@ void internal::basic_writer<Range>::write_fp(T value,
const format_specs& specs) {
// Check type.
float_spec_handler handler(static_cast<char>(specs.type));
internal::handle_float_type_spec(handler.type, handler);
handle_float_type_spec(handler.type, handler);
char sign = 0;
auto sign = specs.sign;
// Use signbit instead of value < 0 since the latter is always false for NaN.
if (std::signbit(value)) {
sign = '-';
sign = sign::minus;
value = -value;
} else if (specs.sign != sign::none) {
if (specs.sign == sign::plus)
sign = '+';
else if (specs.sign == sign::space)
sign = ' ';
} else if (sign == sign::minus) {
sign = sign::none;
}
if (!std::isfinite(value)) {
@ -2809,17 +2804,15 @@ void internal::basic_writer<Range>::write_fp(T value,
int exp = 0;
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
unsigned options = 0;
if (handler.fixed) options |= internal::grisu_options::fixed;
if (sizeof(value) == sizeof(float))
options |= internal::grisu_options::binary32;
bool use_grisu = USE_GRISU &&
(specs.type != 'a' && specs.type != 'A' &&
specs.type != 'e' && specs.type != 'E') &&
internal::grisu_format(static_cast<double>(value), buffer,
precision, options, exp);
if (handler.fixed) options |= grisu_options::fixed;
if (sizeof(value) == sizeof(float)) options |= grisu_options::binary32;
bool use_grisu =
USE_GRISU &&
(specs.type != 'a' && specs.type != 'A' && specs.type != 'e' &&
specs.type != 'E') &&
grisu_format(static_cast<double>(value), buffer, precision, options, exp);
char* decimal_point_pos = nullptr;
if (!use_grisu)
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
if (!use_grisu) decimal_point_pos = sprintf_format(value, buffer, specs);
if (handler.as_percentage) {
buffer.push_back('%');
@ -2829,8 +2822,8 @@ void internal::basic_writer<Range>::write_fp(T value,
if (specs.align == align::numeric) {
if (sign) {
auto&& it = reserve(1);
*it++ = static_cast<char_type>(sign);
sign = 0;
*it++ = static_cast<char_type>(data::signs[sign]);
sign = sign::none;
if (as.width) --as.width;
}
as.align = align::right;
@ -2841,12 +2834,14 @@ void internal::basic_writer<Range>::write_fp(T value,
? internal::decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
if (use_grisu) {
auto params = internal::gen_digits_params();
auto params = gen_digits_params();
params.sign = sign;
params.fixed = handler.fixed;
params.num_digits = precision;
params.trailing_zeros =
(precision != 0 && (handler.fixed || !specs.type)) || specs.alt;
write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
write_padded(as,
grisu_writer<char_type>(buffer, exp, params, decimal_point));
} else {
write_padded(as,
double_writer{sign, buffer, decimal_point_pos, decimal_point});