mirror of
https://github.com/fmtlib/fmt.git
synced 2025-06-25 01:11:40 +02:00
Cleanup FP formatting
This commit is contained in:
@ -2355,8 +2355,6 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand,
|
||||
buffer.end(), out);
|
||||
}
|
||||
|
||||
enum { exp_lower = -4 };
|
||||
|
||||
// Numbers with exponents greater or equal to the returned value will use
|
||||
// the exponential notation.
|
||||
template <typename T> constexpr auto exp_upper() -> int {
|
||||
@ -2365,105 +2363,10 @@ template <typename T> constexpr auto exp_upper() -> int {
|
||||
: 16;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename DecimalFP,
|
||||
typename Grouping = digit_grouping<Char>>
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs& specs, sign s,
|
||||
int exp_upper, locale_ref loc) -> OutputIt {
|
||||
auto significand = f.significand;
|
||||
int significand_size = get_significand_size(f);
|
||||
const Char zero = static_cast<Char>('0');
|
||||
size_t size = to_unsigned(significand_size) + (s != sign::none ? 1 : 0);
|
||||
using iterator = reserve_iterator<OutputIt>;
|
||||
|
||||
Char decimal_point = specs.localized() ? detail::decimal_point<Char>(loc)
|
||||
: static_cast<Char>('.');
|
||||
|
||||
int output_exp = f.exponent + significand_size - 1;
|
||||
auto use_exp_format = [=]() {
|
||||
if (specs.type() == presentation_type::exp) return true;
|
||||
if (specs.type() == presentation_type::fixed) return false;
|
||||
// Use the fixed notation if the exponent is in [exp_lower, exp_upper),
|
||||
// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
|
||||
return output_exp < exp_lower ||
|
||||
output_exp >= (specs.precision > 0 ? specs.precision : exp_upper);
|
||||
};
|
||||
if (use_exp_format()) {
|
||||
int num_zeros = 0;
|
||||
if (specs.alt()) {
|
||||
num_zeros = specs.precision - significand_size;
|
||||
if (num_zeros < 0) num_zeros = 0;
|
||||
size += to_unsigned(num_zeros);
|
||||
} else if (significand_size == 1) {
|
||||
decimal_point = Char();
|
||||
}
|
||||
size += to_unsigned((decimal_point ? 1 : 0) + compute_exp_size(output_exp));
|
||||
char exp_char = specs.upper() ? 'E' : 'e';
|
||||
auto write = [=](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
it = write_significand(it, significand, significand_size, 1,
|
||||
decimal_point);
|
||||
if (num_zeros > 0) it = detail::fill_n(it, num_zeros, zero);
|
||||
*it++ = static_cast<Char>(exp_char);
|
||||
return write_exponent<Char>(output_exp, it);
|
||||
};
|
||||
return specs.width > 0
|
||||
? write_padded<Char, align::right>(out, specs, size, write)
|
||||
: base_iterator(out, write(reserve(out, size)));
|
||||
}
|
||||
|
||||
int exp = f.exponent + significand_size;
|
||||
if (f.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
size += to_unsigned(f.exponent);
|
||||
int num_zeros = specs.precision - exp;
|
||||
abort_fuzzing_if(num_zeros > 5000);
|
||||
if (specs.alt()) {
|
||||
++size;
|
||||
if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
|
||||
num_zeros = 0;
|
||||
if (num_zeros > 0) size += to_unsigned(num_zeros);
|
||||
}
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand<Char>(it, significand, significand_size,
|
||||
f.exponent, grouping);
|
||||
if (!specs.alt()) return it;
|
||||
*it++ = decimal_point;
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
});
|
||||
} else if (exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
|
||||
size += 1 + static_cast<unsigned>(max_of(num_zeros, 0));
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += to_unsigned(grouping.count_separators(exp));
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand(it, significand, significand_size, exp,
|
||||
decimal_point, grouping);
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it;
|
||||
});
|
||||
}
|
||||
// 1234e-6 -> 0.001234
|
||||
int num_zeros = -exp;
|
||||
if (significand_size == 0 && specs.precision >= 0 &&
|
||||
specs.precision < num_zeros) {
|
||||
num_zeros = specs.precision;
|
||||
}
|
||||
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
||||
size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros);
|
||||
return write_padded<Char, align::right>(out, specs, size, [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
*it++ = zero;
|
||||
if (!pointy) return it;
|
||||
*it++ = decimal_point;
|
||||
it = detail::fill_n(it, num_zeros, zero);
|
||||
return write_significand<Char>(it, significand, significand_size);
|
||||
});
|
||||
// Use the fixed notation if the exponent is in [-4, exp_upper),
|
||||
// e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation.
|
||||
constexpr auto use_fixed(int exp, int exp_upper) -> bool {
|
||||
return exp >= -4 && exp < exp_upper;
|
||||
}
|
||||
|
||||
template <typename Char> class fallback_digit_grouping {
|
||||
@ -2480,16 +2383,122 @@ template <typename Char> class fallback_digit_grouping {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Grouping, typename OutputIt,
|
||||
typename DecimalFP>
|
||||
FMT_CONSTEXPR20 auto write_fixed(OutputIt out, const DecimalFP& f,
|
||||
int significand_size, Char decimal_point,
|
||||
const format_specs& specs, sign s,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
using iterator = reserve_iterator<OutputIt>;
|
||||
|
||||
int exp = f.exponent + significand_size;
|
||||
int size = significand_size + (s != sign::none ? 1 : 0);
|
||||
if (f.exponent >= 0) {
|
||||
// 1234e5 -> 123400000[.0+]
|
||||
size += f.exponent;
|
||||
int num_zeros = specs.precision - exp;
|
||||
abort_fuzzing_if(num_zeros > 5000);
|
||||
if (specs.alt()) {
|
||||
++size;
|
||||
if (num_zeros <= 0 && specs.type() != presentation_type::fixed)
|
||||
num_zeros = 0;
|
||||
if (num_zeros > 0) size += num_zeros;
|
||||
}
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += grouping.count_separators(exp);
|
||||
return write_padded<Char, align::right>(
|
||||
out, specs, to_unsigned(size), [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand<Char>(it, f.significand, significand_size,
|
||||
f.exponent, grouping);
|
||||
if (!specs.alt()) return it;
|
||||
*it++ = decimal_point;
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it;
|
||||
});
|
||||
}
|
||||
if (exp > 0) {
|
||||
// 1234e-2 -> 12.34[0+]
|
||||
int num_zeros = specs.alt() ? specs.precision - significand_size : 0;
|
||||
size += 1 + max_of(num_zeros, 0);
|
||||
auto grouping = Grouping(loc, specs.localized());
|
||||
size += grouping.count_separators(exp);
|
||||
return write_padded<Char, align::right>(
|
||||
out, specs, to_unsigned(size), [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
it = write_significand(it, f.significand, significand_size, exp,
|
||||
decimal_point, grouping);
|
||||
return num_zeros > 0 ? detail::fill_n(it, num_zeros, Char('0')) : it;
|
||||
});
|
||||
}
|
||||
// 1234e-6 -> 0.001234
|
||||
int num_zeros = -exp;
|
||||
if (significand_size == 0 && specs.precision >= 0 &&
|
||||
specs.precision < num_zeros) {
|
||||
num_zeros = specs.precision;
|
||||
}
|
||||
bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt();
|
||||
size += 1 + (pointy ? 1 : 0) + num_zeros;
|
||||
return write_padded<Char, align::right>(
|
||||
out, specs, to_unsigned(size), [&](iterator it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
*it++ = Char('0');
|
||||
if (!pointy) return it;
|
||||
*it++ = decimal_point;
|
||||
it = detail::fill_n(it, num_zeros, Char('0'));
|
||||
return write_significand<Char>(it, f.significand, significand_size);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Char, typename Grouping, typename OutputIt,
|
||||
typename DecimalFP>
|
||||
FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs& specs, sign s,
|
||||
int exp_upper, locale_ref loc) -> OutputIt {
|
||||
Char point = specs.localized() ? detail::decimal_point<Char>(loc) : Char('.');
|
||||
int significand_size = get_significand_size(f);
|
||||
int exp = f.exponent + significand_size - 1;
|
||||
if (specs.type() == presentation_type::fixed ||
|
||||
(specs.type() != presentation_type::exp &&
|
||||
use_fixed(exp, specs.precision > 0 ? specs.precision : exp_upper))) {
|
||||
return write_fixed<Char, Grouping>(out, f, significand_size, point, specs,
|
||||
s, loc);
|
||||
}
|
||||
|
||||
// Write value in the exponential format.
|
||||
int num_zeros = 0;
|
||||
int size = significand_size + (s != sign::none ? 1 : 0);
|
||||
if (specs.alt()) {
|
||||
num_zeros = max_of(specs.precision - significand_size, 0);
|
||||
size += num_zeros;
|
||||
} else if (significand_size == 1) {
|
||||
point = Char();
|
||||
}
|
||||
size += (point ? 1 : 0) + compute_exp_size(exp);
|
||||
char exp_char = specs.upper() ? 'E' : 'e';
|
||||
auto write = [=](reserve_iterator<OutputIt> it) {
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
it = write_significand(it, f.significand, significand_size, 1, point);
|
||||
if (num_zeros > 0) it = detail::fill_n(it, num_zeros, Char('0'));
|
||||
*it++ = Char(exp_char);
|
||||
return write_exponent<Char>(exp, it);
|
||||
};
|
||||
auto usize = to_unsigned(size);
|
||||
return specs.width > 0
|
||||
? write_padded<Char, align::right>(out, specs, usize, write)
|
||||
: base_iterator(out, write(reserve(out, usize)));
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename DecimalFP>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f,
|
||||
const format_specs& specs, sign s,
|
||||
int exp_upper, locale_ref loc) -> OutputIt {
|
||||
if (is_constant_evaluated()) {
|
||||
return do_write_float<Char, OutputIt, DecimalFP,
|
||||
fallback_digit_grouping<Char>>(out, f, specs, s,
|
||||
exp_upper, loc);
|
||||
return do_write_float<Char, fallback_digit_grouping<Char>>(out, f, specs, s,
|
||||
exp_upper, loc);
|
||||
} else {
|
||||
return do_write_float<Char>(out, f, specs, s, exp_upper, loc);
|
||||
return do_write_float<Char, digit_grouping<Char>>(out, f, specs, s,
|
||||
exp_upper, loc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3321,9 +3330,12 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision,
|
||||
return exp;
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
locale_ref loc) -> OutputIt {
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
if (specs.localized() && write_loc(out, value, specs, loc)) return out;
|
||||
|
||||
// Use signbit because value < 0 is false for NaN.
|
||||
sign s = detail::signbit(value) ? sign::minus : specs.sign();
|
||||
|
||||
@ -3336,7 +3348,7 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
if (specs.width != 0) --specs.width;
|
||||
}
|
||||
|
||||
constexpr int exp_upper = detail::exp_upper<T>();
|
||||
const int exp_upper = detail::exp_upper<T>();
|
||||
int precision = specs.precision;
|
||||
if (precision < 0) {
|
||||
if (specs.type() != presentation_type::none) {
|
||||
@ -3376,15 +3388,6 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs,
|
||||
return write_float<Char>(out, f, specs, s, exp_upper, loc);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_floating_point<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
|
||||
locale_ref loc = {}) -> OutputIt {
|
||||
return specs.localized() && write_loc(out, value, specs, loc)
|
||||
? out
|
||||
: write_float<Char>(out, value, specs, loc);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_fast_float<T>::value)>
|
||||
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
|
||||
@ -3402,29 +3405,31 @@ FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
|
||||
auto dec = dragonbox::to_decimal(static_cast<floaty>(value));
|
||||
int significand_size = count_digits(dec.significand);
|
||||
int exp = dec.exponent + significand_size - 1;
|
||||
int exp_upper = detail::exp_upper<T>();
|
||||
if (exp < exp_lower || exp >= exp_upper) {
|
||||
auto has_decimal_point = significand_size != 1;
|
||||
size_t size =
|
||||
to_unsigned((s != sign::none ? 1 : 0) + significand_size +
|
||||
(has_decimal_point ? 1 : 0) + compute_exp_size(exp));
|
||||
auto it = reserve(out, size);
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
it = write_significand(it, dec.significand, significand_size, 1,
|
||||
has_decimal_point ? '.' : Char());
|
||||
*it++ = static_cast<Char>('e');
|
||||
it = write_exponent<Char>(exp, it);
|
||||
return base_iterator(out, it);
|
||||
if (use_fixed(exp, detail::exp_upper<T>())) {
|
||||
return write_fixed<Char, fallback_digit_grouping<Char>>(
|
||||
out, dec, significand_size, Char('.'), specs, s);
|
||||
}
|
||||
return write_float<Char>(out, dec, specs, s, exp_upper, {});
|
||||
|
||||
// Write value in the exponential format.
|
||||
auto has_decimal_point = significand_size != 1;
|
||||
size_t size =
|
||||
to_unsigned((s != sign::none ? 1 : 0) + significand_size +
|
||||
(has_decimal_point ? 1 : 0) + compute_exp_size(exp));
|
||||
auto it = reserve(out, size);
|
||||
if (s != sign::none) *it++ = detail::getsign<Char>(s);
|
||||
// Insert a decimal point after the first digit and add an exponent.
|
||||
it = write_significand(it, dec.significand, significand_size, 1,
|
||||
has_decimal_point ? '.' : Char());
|
||||
*it++ = static_cast<Char>('e');
|
||||
it = write_exponent<Char>(exp, it);
|
||||
return base_iterator(out, it);
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt, typename T,
|
||||
FMT_ENABLE_IF(is_floating_point<T>::value &&
|
||||
!is_fast_float<T>::value)>
|
||||
inline auto write(OutputIt out, T value) -> OutputIt {
|
||||
return write<Char>(out, value, format_specs());
|
||||
return write<Char>(out, value, {});
|
||||
}
|
||||
|
||||
template <typename Char, typename OutputIt>
|
||||
|
Reference in New Issue
Block a user