Improve handling of signs
This commit is contained in:
parent
8498bc97dd
commit
a967dcbe20
@ -342,6 +342,7 @@ template <typename T>
|
|||||||
const char basic_data<T>::background_color[] = "\x1b[48;2;";
|
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 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 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 {
|
template <typename T> struct bits {
|
||||||
static FMT_CONSTEXPR_DECL const int value =
|
static FMT_CONSTEXPR_DECL const int value =
|
||||||
|
@ -670,6 +670,7 @@ template <typename T = void> struct FMT_EXTERN_TEMPLATE_API basic_data {
|
|||||||
static const char background_color[];
|
static const char background_color[];
|
||||||
static const char reset_color[5];
|
static const char reset_color[5];
|
||||||
static const wchar_t wreset_color[5];
|
static const wchar_t wreset_color[5];
|
||||||
|
static const char signs[];
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_EXTERN template struct basic_data<void>;
|
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 {
|
struct gen_digits_params {
|
||||||
int num_digits;
|
int num_digits;
|
||||||
|
sign_t sign : 3;
|
||||||
bool fixed;
|
bool fixed;
|
||||||
bool upper;
|
bool upper;
|
||||||
bool trailing_zeros;
|
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
|
// This template provides operations for formatting and writing data into a
|
||||||
// character range.
|
// character range.
|
||||||
template <typename Range> class basic_writer {
|
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.
|
enum { inf_size = 3 }; // This is an enum to workaround a bug in MSVC.
|
||||||
|
|
||||||
struct inf_or_nan_writer {
|
struct inf_or_nan_writer {
|
||||||
char sign;
|
sign_t sign;
|
||||||
bool as_percentage;
|
bool as_percentage;
|
||||||
const char* str;
|
const char* str;
|
||||||
|
|
||||||
@ -1521,7 +1558,7 @@ template <typename Range> class basic_writer {
|
|||||||
size_t width() const { return size(); }
|
size_t width() const { return size(); }
|
||||||
|
|
||||||
template <typename It> void operator()(It&& it) const {
|
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>(
|
it = internal::copy_str<char_type>(
|
||||||
str, str + static_cast<std::size_t>(inf_size), it);
|
str, str + static_cast<std::size_t>(inf_size), it);
|
||||||
if (as_percentage) *it++ = static_cast<char_type>('%');
|
if (as_percentage) *it++ = static_cast<char_type>('%');
|
||||||
@ -1529,7 +1566,7 @@ template <typename Range> class basic_writer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct double_writer {
|
struct double_writer {
|
||||||
char sign;
|
sign_t sign;
|
||||||
internal::buffer<char>& buffer;
|
internal::buffer<char>& buffer;
|
||||||
char* decimal_point_pos;
|
char* decimal_point_pos;
|
||||||
char_type decimal_point;
|
char_type decimal_point;
|
||||||
@ -1538,7 +1575,7 @@ template <typename Range> class basic_writer {
|
|||||||
size_t width() const { return size(); }
|
size_t width() const { return size(); }
|
||||||
|
|
||||||
template <typename It> void operator()(It&& it) {
|
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();
|
auto begin = buffer.begin();
|
||||||
if (decimal_point_pos) {
|
if (decimal_point_pos) {
|
||||||
it = internal::copy_str<char_type>(begin, decimal_point_pos, it);
|
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 {
|
template <typename Char> struct str_writer {
|
||||||
const Char* s;
|
const Char* s;
|
||||||
size_t size_;
|
size_t size_;
|
||||||
@ -2780,18 +2778,15 @@ void internal::basic_writer<Range>::write_fp(T value,
|
|||||||
const format_specs& specs) {
|
const format_specs& specs) {
|
||||||
// Check type.
|
// Check type.
|
||||||
float_spec_handler handler(static_cast<char>(specs.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.
|
// Use signbit instead of value < 0 since the latter is always false for NaN.
|
||||||
if (std::signbit(value)) {
|
if (std::signbit(value)) {
|
||||||
sign = '-';
|
sign = sign::minus;
|
||||||
value = -value;
|
value = -value;
|
||||||
} else if (specs.sign != sign::none) {
|
} else if (sign == sign::minus) {
|
||||||
if (specs.sign == sign::plus)
|
sign = sign::none;
|
||||||
sign = '+';
|
|
||||||
else if (specs.sign == sign::space)
|
|
||||||
sign = ' ';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!std::isfinite(value)) {
|
if (!std::isfinite(value)) {
|
||||||
@ -2809,17 +2804,15 @@ void internal::basic_writer<Range>::write_fp(T value,
|
|||||||
int exp = 0;
|
int exp = 0;
|
||||||
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
|
int precision = specs.precision >= 0 || !specs.type ? specs.precision : 6;
|
||||||
unsigned options = 0;
|
unsigned options = 0;
|
||||||
if (handler.fixed) options |= internal::grisu_options::fixed;
|
if (handler.fixed) options |= grisu_options::fixed;
|
||||||
if (sizeof(value) == sizeof(float))
|
if (sizeof(value) == sizeof(float)) options |= grisu_options::binary32;
|
||||||
options |= internal::grisu_options::binary32;
|
bool use_grisu =
|
||||||
bool use_grisu = USE_GRISU &&
|
USE_GRISU &&
|
||||||
(specs.type != 'a' && specs.type != 'A' &&
|
(specs.type != 'a' && specs.type != 'A' && specs.type != 'e' &&
|
||||||
specs.type != 'e' && specs.type != 'E') &&
|
specs.type != 'E') &&
|
||||||
internal::grisu_format(static_cast<double>(value), buffer,
|
grisu_format(static_cast<double>(value), buffer, precision, options, exp);
|
||||||
precision, options, exp);
|
|
||||||
char* decimal_point_pos = nullptr;
|
char* decimal_point_pos = nullptr;
|
||||||
if (!use_grisu)
|
if (!use_grisu) decimal_point_pos = sprintf_format(value, buffer, specs);
|
||||||
decimal_point_pos = internal::sprintf_format(value, buffer, specs);
|
|
||||||
|
|
||||||
if (handler.as_percentage) {
|
if (handler.as_percentage) {
|
||||||
buffer.push_back('%');
|
buffer.push_back('%');
|
||||||
@ -2829,8 +2822,8 @@ void internal::basic_writer<Range>::write_fp(T value,
|
|||||||
if (specs.align == align::numeric) {
|
if (specs.align == align::numeric) {
|
||||||
if (sign) {
|
if (sign) {
|
||||||
auto&& it = reserve(1);
|
auto&& it = reserve(1);
|
||||||
*it++ = static_cast<char_type>(sign);
|
*it++ = static_cast<char_type>(data::signs[sign]);
|
||||||
sign = 0;
|
sign = sign::none;
|
||||||
if (as.width) --as.width;
|
if (as.width) --as.width;
|
||||||
}
|
}
|
||||||
as.align = align::right;
|
as.align = align::right;
|
||||||
@ -2841,12 +2834,14 @@ void internal::basic_writer<Range>::write_fp(T value,
|
|||||||
? internal::decimal_point<char_type>(locale_)
|
? internal::decimal_point<char_type>(locale_)
|
||||||
: static_cast<char_type>('.');
|
: static_cast<char_type>('.');
|
||||||
if (use_grisu) {
|
if (use_grisu) {
|
||||||
auto params = internal::gen_digits_params();
|
auto params = gen_digits_params();
|
||||||
|
params.sign = sign;
|
||||||
params.fixed = handler.fixed;
|
params.fixed = handler.fixed;
|
||||||
params.num_digits = precision;
|
params.num_digits = precision;
|
||||||
params.trailing_zeros =
|
params.trailing_zeros =
|
||||||
(precision != 0 && (handler.fixed || !specs.type)) || specs.alt;
|
(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 {
|
} else {
|
||||||
write_padded(as,
|
write_padded(as,
|
||||||
double_writer{sign, buffer, decimal_point_pos, decimal_point});
|
double_writer{sign, buffer, decimal_point_pos, decimal_point});
|
||||||
|
Loading…
Reference in New Issue
Block a user