forked from fmtlib/fmt
Improve floating-point formatting
This commit is contained in:
@@ -232,31 +232,20 @@ 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 ?
|
return precision < 0 ?
|
||||||
FMT_SNPRINTF(buffer, size, format, value) :
|
FMT_SNPRINTF(buffer, size, format, value) :
|
||||||
FMT_SNPRINTF(buffer, size, format, precision, value);
|
FMT_SNPRINTF(buffer, size, format, precision, value);
|
||||||
}
|
}
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SNPRINTF(buffer, size, format, width, value) :
|
|
||||||
FMT_SNPRINTF(buffer, size, format, width, 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 ?
|
return precision < 0 ?
|
||||||
FMT_SWPRINTF(buffer, size, format, value) :
|
FMT_SWPRINTF(buffer, size, format, value) :
|
||||||
FMT_SWPRINTF(buffer, size, format, precision, value);
|
FMT_SWPRINTF(buffer, size, format, precision, value);
|
||||||
}
|
}
|
||||||
return precision < 0 ?
|
|
||||||
FMT_SWPRINTF(buffer, size, format, width, value) :
|
|
||||||
FMT_SWPRINTF(buffer, size, format, width, precision, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
const char basic_data<T>::DIGITS[] =
|
const char basic_data<T>::DIGITS[] =
|
||||||
|
@@ -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 {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
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) {
|
|||||||
} 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>
|
|||||||
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(
|
|||||||
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(
|
|||||||
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(
|
|||||||
|
|
||||||
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):
|
|||||||
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:]))
|
||||||
|
Reference in New Issue
Block a user