mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-09 06:00:07 +00:00
Implement simple version of Grisu
This commit is contained in:
parent
4027557958
commit
4e4b8570e5
@ -346,7 +346,7 @@ FMT_FUNC fp operator*(fp x, fp y) {
|
||||
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||
int index = static_cast<int>(std::ceil(
|
||||
(min_exponent + fp::fp_significand_size - 1) * one_over_log2_10));
|
||||
(min_exponent + fp::significand_size - 1) * one_over_log2_10));
|
||||
// Decimal exponent of the first (smallest) cached power of 10.
|
||||
const int first_dec_exp = -348;
|
||||
// Difference between two consecutive decimal exponents in cached powers of 10.
|
||||
|
@ -132,10 +132,15 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || FMT_MSC_VER >= 1600
|
||||
#if FMT_HAS_GXX_CXX11 || FMT_HAS_FEATURE(cxx_trailing_return) || \
|
||||
FMT_MSC_VER >= 1600
|
||||
# define FMT_USE_TRAILING_RETURN 1
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_GRISU
|
||||
# define FMT_USE_GRISU 0
|
||||
#endif
|
||||
|
||||
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
||||
// https://github.com/fmtlib/fmt/issues/519
|
||||
#ifndef _MSC_VER
|
||||
@ -274,8 +279,7 @@ class fp {
|
||||
significand_type f;
|
||||
int e;
|
||||
|
||||
static constexpr int fp_significand_size =
|
||||
sizeof(significand_type) * char_size;
|
||||
static constexpr int significand_size = sizeof(significand_type) * char_size;
|
||||
|
||||
fp(uint64_t f, int e): f(f), e(e) {}
|
||||
|
||||
@ -311,7 +315,7 @@ class fp {
|
||||
--e;
|
||||
}
|
||||
// Subtract 1 to account for hidden bit.
|
||||
auto offset = fp_significand_size - double_significand_size - SHIFT - 1;
|
||||
auto offset = significand_size - double_significand_size - SHIFT - 1;
|
||||
f <<= offset;
|
||||
e -= offset;
|
||||
}
|
||||
@ -395,6 +399,38 @@ FMT_BEGIN_NAMESPACE
|
||||
template <typename Range>
|
||||
class basic_writer;
|
||||
|
||||
template <typename OutputIt, typename T = typename OutputIt::value_type>
|
||||
class output_range {
|
||||
private:
|
||||
OutputIt it_;
|
||||
|
||||
// Unused yet.
|
||||
typedef void sentinel;
|
||||
sentinel end() const;
|
||||
|
||||
public:
|
||||
typedef OutputIt iterator;
|
||||
typedef T value_type;
|
||||
|
||||
explicit output_range(OutputIt it): it_(it) {}
|
||||
OutputIt begin() const { return it_; }
|
||||
};
|
||||
|
||||
// A range where begin() returns back_insert_iterator.
|
||||
template <typename Container>
|
||||
class back_insert_range:
|
||||
public output_range<std::back_insert_iterator<Container>> {
|
||||
typedef output_range<std::back_insert_iterator<Container>> base;
|
||||
public:
|
||||
typedef typename Container::value_type value_type;
|
||||
|
||||
back_insert_range(Container &c): base(std::back_inserter(c)) {}
|
||||
back_insert_range(typename base::iterator it): base(it) {}
|
||||
};
|
||||
|
||||
typedef basic_writer<back_insert_range<internal::buffer>> writer;
|
||||
typedef basic_writer<back_insert_range<internal::wbuffer>> wwriter;
|
||||
|
||||
/** A formatting error such as invalid format string. */
|
||||
class format_error : public std::runtime_error {
|
||||
public:
|
||||
@ -2619,6 +2655,10 @@ class basic_writer {
|
||||
// Formats a floating-point number (double or long double).
|
||||
template <typename T>
|
||||
void write_double(T value, const format_specs &spec);
|
||||
template <typename T>
|
||||
void write_double_sprintf(T value, const format_specs &spec,
|
||||
internal::basic_buffer<char_type>& buffer,
|
||||
char sign);
|
||||
|
||||
template <typename Char>
|
||||
struct str_writer {
|
||||
@ -2841,7 +2881,60 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
||||
|
||||
basic_memory_buffer<char_type> buffer;
|
||||
if (FMT_USE_GRISU && sizeof(T) <= sizeof(double) &&
|
||||
std::numeric_limits<double>::is_iec559) {
|
||||
internal::fp fp_value(static_cast<double>(value));
|
||||
fp_value.normalize();
|
||||
// Find a cached power of 10 close to 1 / fp_value.
|
||||
int dec_exp = 0;
|
||||
int min_exp = -60;
|
||||
auto dec_pow = internal::get_cached_power(
|
||||
min_exp - (fp_value.e + internal::fp::significand_size), dec_exp);
|
||||
internal::fp product = fp_value * dec_pow;
|
||||
// Generate output.
|
||||
internal::fp one(1ull << -product.e, product.e);
|
||||
uint32_t hi = product.f >> -one.e;
|
||||
uint64_t f = product.f & (one.f - 1);
|
||||
typedef back_insert_range<internal::basic_buffer<char_type>> range;
|
||||
basic_writer<range> w{range(buffer)};
|
||||
w.write(hi);
|
||||
w.write('.');
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
f *= 10;
|
||||
w.write(static_cast<char>('0' + (f >> -one.e)));
|
||||
f &= one.f - 1;
|
||||
}
|
||||
w.write('e');
|
||||
w.write(-dec_exp);
|
||||
} else {
|
||||
format_specs normalized_spec(spec);
|
||||
normalized_spec.type_ = handler.type;
|
||||
write_double_sprintf(value, normalized_spec, buffer, sign);
|
||||
}
|
||||
unsigned n = buffer.size();
|
||||
align_spec as = spec;
|
||||
if (spec.align() == ALIGN_NUMERIC) {
|
||||
if (sign) {
|
||||
*reserve(1) = sign;
|
||||
sign = 0;
|
||||
if (as.width_)
|
||||
--as.width_;
|
||||
}
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_DEFAULT)
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
if (sign)
|
||||
++n;
|
||||
}
|
||||
write_padded(n, as, double_writer{n, sign, buffer});
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
template <typename T>
|
||||
void basic_writer<Range>::write_double_sprintf(
|
||||
T value, const format_specs &spec,
|
||||
internal::basic_buffer<char_type>& buffer, char sign) {
|
||||
unsigned width = spec.width();
|
||||
if (sign) {
|
||||
buffer.reserve(width > 1u ? width : 1u);
|
||||
@ -2864,11 +2957,10 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
}
|
||||
|
||||
append_float_length(format_ptr, value);
|
||||
*format_ptr++ = handler.type;
|
||||
*format_ptr++ = spec.type();
|
||||
*format_ptr = '\0';
|
||||
|
||||
// Format using snprintf.
|
||||
unsigned n = 0;
|
||||
char_type *start = FMT_NULL;
|
||||
for (;;) {
|
||||
std::size_t buffer_size = buffer.capacity();
|
||||
@ -2885,9 +2977,11 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
int result = internal::char_traits<char_type>::format_float(
|
||||
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
||||
if (result >= 0) {
|
||||
n = internal::to_unsigned(result);
|
||||
if (n < buffer.capacity())
|
||||
unsigned n = internal::to_unsigned(result);
|
||||
if (n < buffer.capacity()) {
|
||||
buffer.resize(n);
|
||||
break; // The buffer is large enough - continue with formatting.
|
||||
}
|
||||
buffer.reserve(n + 1);
|
||||
} else {
|
||||
// If result is negative we ask to increase the capacity by at least 1,
|
||||
@ -2895,56 +2989,8 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
|
||||
buffer.reserve(buffer.capacity() + 1);
|
||||
}
|
||||
}
|
||||
align_spec as = spec;
|
||||
if (spec.align() == ALIGN_NUMERIC) {
|
||||
if (sign) {
|
||||
*reserve(1) = sign;
|
||||
sign = 0;
|
||||
if (as.width_)
|
||||
--as.width_;
|
||||
}
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
} else {
|
||||
if (spec.align() == ALIGN_DEFAULT)
|
||||
as.align_ = ALIGN_RIGHT;
|
||||
if (sign)
|
||||
++n;
|
||||
}
|
||||
write_padded(n, as, double_writer{n, sign, buffer});
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename T = typename OutputIt::value_type>
|
||||
class output_range {
|
||||
private:
|
||||
OutputIt it_;
|
||||
|
||||
// Unused yet.
|
||||
typedef void sentinel;
|
||||
sentinel end() const;
|
||||
|
||||
public:
|
||||
typedef OutputIt iterator;
|
||||
typedef T value_type;
|
||||
|
||||
explicit output_range(OutputIt it): it_(it) {}
|
||||
OutputIt begin() const { return it_; }
|
||||
};
|
||||
|
||||
// A range where begin() returns back_insert_iterator.
|
||||
template <typename Container>
|
||||
class back_insert_range:
|
||||
public output_range<std::back_insert_iterator<Container>> {
|
||||
typedef output_range<std::back_insert_iterator<Container>> base;
|
||||
public:
|
||||
typedef typename Container::value_type value_type;
|
||||
|
||||
back_insert_range(Container &c): base(std::back_inserter(c)) {}
|
||||
back_insert_range(typename base::iterator it): base(it) {}
|
||||
};
|
||||
|
||||
typedef basic_writer<back_insert_range<internal::buffer>> writer;
|
||||
typedef basic_writer<back_insert_range<internal::wbuffer>> wwriter;
|
||||
|
||||
// Reports a system error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_system_error(int error_code,
|
||||
|
Loading…
Reference in New Issue
Block a user