Commit b60a5c5d authored by Victor Zverovich's avatar Victor Zverovich

Improve floating-point formatting

parent 8dc2360b
...@@ -232,30 +232,19 @@ FMT_FUNC void system_error::init( ...@@ -232,30 +232,19 @@ FMT_FUNC void system_error::init(
namespace internal { namespace internal {
template <typename T> template <typename T>
int char_traits<char>::format_float( int char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format, int precision, T value) {
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ? return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) : FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value); FMT_SNPRINTF(buffer, size, format, precision, value);
} }
template <typename T> template <typename T>
int char_traits<wchar_t>::format_float( int char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format, int precision,
unsigned width, int precision, T value) { T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ? return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) : FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value); FMT_SWPRINTF(buffer, size, format, precision, value);
} }
template <typename T> template <typename T>
......
...@@ -660,30 +660,30 @@ struct char_traits<char> { ...@@ -660,30 +660,30 @@ struct char_traits<char> {
// Formats a floating-point number. // Formats a floating-point number.
template <typename T> template <typename T>
FMT_API static int format_float(char *buffer, std::size_t size, FMT_API static int format_float(char *buffer, std::size_t size,
const char *format, unsigned width, int precision, T value); const char *format, int precision, T value);
}; };
template <> template <>
struct char_traits<wchar_t> { struct char_traits<wchar_t> {
template <typename T> template <typename T>
FMT_API static int format_float(wchar_t *buffer, std::size_t size, FMT_API static int format_float(wchar_t *buffer, std::size_t size,
const wchar_t *format, unsigned width, int precision, T value); const wchar_t *format, int precision, T value);
}; };
#if FMT_USE_EXTERN_TEMPLATES #if FMT_USE_EXTERN_TEMPLATES
extern template int char_traits<char>::format_float<double>( extern template int char_traits<char>::format_float<double>(
char *buffer, std::size_t size, const char* format, unsigned width, char *buffer, std::size_t size, const char* format, int precision,
int precision, double value); double value);
extern template int char_traits<char>::format_float<long double>( extern template int char_traits<char>::format_float<long double>(
char *buffer, std::size_t size, const char* format, unsigned width, char *buffer, std::size_t size, const char* format, int precision,
int precision, long double value); long double value);
extern template int char_traits<wchar_t>::format_float<double>( extern template int char_traits<wchar_t>::format_float<double>(
wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, wchar_t *buffer, std::size_t size, const wchar_t* format, int precision,
int precision, double value); double value);
extern template int char_traits<wchar_t>::format_float<long double>( extern template int char_traits<wchar_t>::format_float<long double>(
wchar_t *buffer, std::size_t size, const wchar_t* format, unsigned width, wchar_t *buffer, std::size_t size, const wchar_t* format, int precision,
int precision, long double value); long double value);
#endif #endif
template <typename Container> template <typename Container>
...@@ -2657,8 +2657,7 @@ class basic_writer { ...@@ -2657,8 +2657,7 @@ class basic_writer {
void write_double(T value, const format_specs &spec); void write_double(T value, const format_specs &spec);
template <typename T> template <typename T>
void write_double_sprintf(T value, const format_specs &spec, void write_double_sprintf(T value, const format_specs &spec,
internal::basic_buffer<char_type>& buffer, internal::basic_buffer<char_type>& buffer);
char sign);
template <typename Char> template <typename Char>
struct str_writer { struct str_writer {
...@@ -2887,7 +2886,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) { ...@@ -2887,7 +2886,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
fp_value.normalize(); fp_value.normalize();
// Find a cached power of 10 close to 1 / fp_value. // Find a cached power of 10 close to 1 / fp_value.
int dec_exp = 0; int dec_exp = 0;
int min_exp = -60; const int min_exp = -60;
auto dec_pow = internal::get_cached_power( auto dec_pow = internal::get_cached_power(
min_exp - (fp_value.e + internal::fp::significand_size), dec_exp); min_exp - (fp_value.e + internal::fp::significand_size), dec_exp);
internal::fp product = fp_value * dec_pow; internal::fp product = fp_value * dec_pow;
...@@ -2898,8 +2897,10 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) { ...@@ -2898,8 +2897,10 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
typedef back_insert_range<internal::basic_buffer<char_type>> range; typedef back_insert_range<internal::basic_buffer<char_type>> range;
basic_writer<range> w{range(buffer)}; basic_writer<range> w{range(buffer)};
w.write(hi); w.write(hi);
unsigned digits = buffer.size();
w.write('.'); w.write('.');
for (int i = 0; i < 18; ++i) { const unsigned max_digits = 18;
while (digits++ < max_digits) {
f *= 10; f *= 10;
w.write(static_cast<char>('0' + (f >> -one.e))); w.write(static_cast<char>('0' + (f >> -one.e)));
f &= one.f - 1; f &= one.f - 1;
...@@ -2909,7 +2910,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) { ...@@ -2909,7 +2910,7 @@ void basic_writer<Range>::write_double(T value, const format_specs &spec) {
} else { } else {
format_specs normalized_spec(spec); format_specs normalized_spec(spec);
normalized_spec.type_ = handler.type; normalized_spec.type_ = handler.type;
write_double_sprintf(value, normalized_spec, buffer, sign); write_double_sprintf(value, normalized_spec, buffer);
} }
unsigned n = buffer.size(); unsigned n = buffer.size();
align_spec as = spec; align_spec as = spec;
...@@ -2934,23 +2935,17 @@ template <typename Range> ...@@ -2934,23 +2935,17 @@ template <typename Range>
template <typename T> template <typename T>
void basic_writer<Range>::write_double_sprintf( void basic_writer<Range>::write_double_sprintf(
T value, const format_specs &spec, T value, const format_specs &spec,
internal::basic_buffer<char_type>& buffer, char sign) { internal::basic_buffer<char_type>& buffer) {
unsigned width = spec.width(); // Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
if (sign) { FMT_ASSERT(buffer.capacity() != 0, "empty buffer");
buffer.reserve(width > 1u ? width : 1u);
if (width > 0)
--width;
}
// Build format string. // Build format string.
enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
char_type format[MAX_FORMAT_SIZE]; char_type format[MAX_FORMAT_SIZE];
char_type *format_ptr = format; char_type *format_ptr = format;
*format_ptr++ = '%'; *format_ptr++ = '%';
unsigned width_for_sprintf = width;
if (spec.flag(HASH_FLAG)) if (spec.flag(HASH_FLAG))
*format_ptr++ = '#'; *format_ptr++ = '#';
width_for_sprintf = 0;
if (spec.precision() >= 0) { if (spec.precision() >= 0) {
*format_ptr++ = '.'; *format_ptr++ = '.';
*format_ptr++ = '*'; *format_ptr++ = '*';
...@@ -2964,18 +2959,9 @@ void basic_writer<Range>::write_double_sprintf( ...@@ -2964,18 +2959,9 @@ void basic_writer<Range>::write_double_sprintf(
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();
#if FMT_MSC_VER
// MSVC's vsnprintf_s doesn't work with zero size, so reserve
// space for at least one extra character to make the size non-zero.
// Note that the buffer's capacity may increase by more than 1.
if (buffer_size == 0) {
buffer.reserve(1);
buffer_size = buffer.capacity();
}
#endif
start = &buffer[0]; start = &buffer[0];
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, spec.precision(), value);
if (result >= 0) { if (result >= 0) {
unsigned n = internal::to_unsigned(result); unsigned n = internal::to_unsigned(result);
if (n < buffer.capacity()) { if (n < buffer.capacity()) {
......
...@@ -21,12 +21,12 @@ template void internal::arg_map<format_context>::init( ...@@ -21,12 +21,12 @@ template void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args); const basic_format_args<format_context> &args);
template FMT_API int internal::char_traits<char>::format_float( template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format, int precision,
unsigned width, int precision, double value); double value);
template FMT_API int internal::char_traits<char>::format_float( template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format, int precision,
unsigned width, int precision, long double value); long double value);
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
...@@ -39,9 +39,9 @@ template void internal::arg_map<wformat_context>::init( ...@@ -39,9 +39,9 @@ template void internal::arg_map<wformat_context>::init(
template FMT_API int internal::char_traits<wchar_t>::format_float( template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value); int precision, double value);
template FMT_API int internal::char_traits<wchar_t>::format_float( template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value); int precision, long double value);
FMT_END_NAMESPACE FMT_END_NAMESPACE
...@@ -48,3 +48,6 @@ for i, fp in enumerate(powers): ...@@ -48,3 +48,6 @@ for i, fp in enumerate(powers):
if i % 11 == 0: if i % 11 == 0:
print(end='\n ') print(end='\n ')
print(' {:5}'.format(fp.e), end=',') print(' {:5}'.format(fp.e), end=',')
print('\n\nMax exponent difference:',
max([x.e - powers[i - 1].e for i, x in enumerate(powers)][1:]))
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