diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 00c97c3f..f02c257e 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -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(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. diff --git a/include/fmt/format.h b/include/fmt/format.h index 0f2baab4..46f06082 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -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 class basic_writer; +template +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 +class back_insert_range: + public output_range> { + typedef output_range> 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> writer; +typedef basic_writer> 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 void write_double(T value, const format_specs &spec); + template + void write_double_sprintf(T value, const format_specs &spec, + internal::basic_buffer& buffer, + char sign); template struct str_writer { @@ -2841,7 +2881,60 @@ void basic_writer::write_double(T value, const format_specs &spec) { return write_inf_or_nan(handler.upper ? "INF" : "inf"); basic_memory_buffer buffer; + if (FMT_USE_GRISU && sizeof(T) <= sizeof(double) && + std::numeric_limits::is_iec559) { + internal::fp fp_value(static_cast(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> range; + basic_writer w{range(buffer)}; + w.write(hi); + w.write('.'); + for (int i = 0; i < 18; ++i) { + f *= 10; + w.write(static_cast('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 +template +void basic_writer::write_double_sprintf( + T value, const format_specs &spec, + internal::basic_buffer& buffer, char sign) { unsigned width = spec.width(); if (sign) { buffer.reserve(width > 1u ? width : 1u); @@ -2864,11 +2957,10 @@ void basic_writer::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::write_double(T value, const format_specs &spec) { int result = internal::char_traits::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::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 -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 -class back_insert_range: - public output_range> { - typedef output_range> 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> writer; -typedef basic_writer> 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,