mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-07 10:20:58 +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) {
|
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent) {
|
||||||
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
|
||||||
int index = static_cast<int>(std::ceil(
|
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.
|
// Decimal exponent of the first (smallest) cached power of 10.
|
||||||
const int first_dec_exp = -348;
|
const int first_dec_exp = -348;
|
||||||
// Difference between two consecutive decimal exponents in cached powers of 10.
|
// Difference between two consecutive decimal exponents in cached powers of 10.
|
||||||
|
@ -132,10 +132,15 @@
|
|||||||
# endif
|
# endif
|
||||||
#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
|
# define FMT_USE_TRAILING_RETURN 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_USE_GRISU
|
||||||
|
# define FMT_USE_GRISU 0
|
||||||
|
#endif
|
||||||
|
|
||||||
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
||||||
// https://github.com/fmtlib/fmt/issues/519
|
// https://github.com/fmtlib/fmt/issues/519
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
@ -274,8 +279,7 @@ class fp {
|
|||||||
significand_type f;
|
significand_type f;
|
||||||
int e;
|
int e;
|
||||||
|
|
||||||
static constexpr int fp_significand_size =
|
static constexpr int significand_size = sizeof(significand_type) * char_size;
|
||||||
sizeof(significand_type) * char_size;
|
|
||||||
|
|
||||||
fp(uint64_t f, int e): f(f), e(e) {}
|
fp(uint64_t f, int e): f(f), e(e) {}
|
||||||
|
|
||||||
@ -311,7 +315,7 @@ class fp {
|
|||||||
--e;
|
--e;
|
||||||
}
|
}
|
||||||
// Subtract 1 to account for hidden bit.
|
// 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;
|
f <<= offset;
|
||||||
e -= offset;
|
e -= offset;
|
||||||
}
|
}
|
||||||
@ -395,6 +399,38 @@ FMT_BEGIN_NAMESPACE
|
|||||||
template <typename Range>
|
template <typename Range>
|
||||||
class basic_writer;
|
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. */
|
/** A formatting error such as invalid format string. */
|
||||||
class format_error : public std::runtime_error {
|
class format_error : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
@ -2619,6 +2655,10 @@ class basic_writer {
|
|||||||
// Formats a floating-point number (double or long double).
|
// Formats a floating-point number (double or long double).
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_double(T value, const format_specs &spec);
|
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>
|
template <typename Char>
|
||||||
struct str_writer {
|
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");
|
return write_inf_or_nan(handler.upper ? "INF" : "inf");
|
||||||
|
|
||||||
basic_memory_buffer<char_type> buffer;
|
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();
|
unsigned width = spec.width();
|
||||||
if (sign) {
|
if (sign) {
|
||||||
buffer.reserve(width > 1u ? width : 1u);
|
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);
|
append_float_length(format_ptr, value);
|
||||||
*format_ptr++ = handler.type;
|
*format_ptr++ = spec.type();
|
||||||
*format_ptr = '\0';
|
*format_ptr = '\0';
|
||||||
|
|
||||||
// Format using snprintf.
|
// Format using snprintf.
|
||||||
unsigned n = 0;
|
|
||||||
char_type *start = FMT_NULL;
|
char_type *start = FMT_NULL;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
std::size_t buffer_size = buffer.capacity();
|
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(
|
int result = internal::char_traits<char_type>::format_float(
|
||||||
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
start, buffer_size, format, width_for_sprintf, spec.precision(), value);
|
||||||
if (result >= 0) {
|
if (result >= 0) {
|
||||||
n = internal::to_unsigned(result);
|
unsigned n = internal::to_unsigned(result);
|
||||||
if (n < buffer.capacity())
|
if (n < buffer.capacity()) {
|
||||||
|
buffer.resize(n);
|
||||||
break; // The buffer is large enough - continue with formatting.
|
break; // The buffer is large enough - continue with formatting.
|
||||||
|
}
|
||||||
buffer.reserve(n + 1);
|
buffer.reserve(n + 1);
|
||||||
} else {
|
} else {
|
||||||
// If result is negative we ask to increase the capacity by at least 1,
|
// 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);
|
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.
|
// Reports a system error without throwing an exception.
|
||||||
// Can be used to report errors from destructors.
|
// Can be used to report errors from destructors.
|
||||||
FMT_API void report_system_error(int error_code,
|
FMT_API void report_system_error(int error_code,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user