Commit 4e4b8570 authored by Victor Zverovich's avatar Victor Zverovich

Implement simple version of Grisu

parent 40275579
...@@ -346,7 +346,7 @@ FMT_FUNC fp operator*(fp x, fp y) { ...@@ -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 @@ ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment