Use the decimal point from locale

This commit is contained in:
Victor Zverovich 2019-07-03 17:25:57 -07:00
parent 476f25cd81
commit bc14c6ee20
5 changed files with 45 additions and 18 deletions

View File

@ -210,12 +210,20 @@ template <typename Char> FMT_FUNC Char thousands_sep_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.thousands_sep(); .thousands_sep();
} }
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref loc) {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>())
.decimal_point();
}
} // namespace internal } // namespace internal
#else #else
template <typename Char> template <typename Char>
FMT_FUNC Char internal::thousands_sep_impl(locale_ref) { FMT_FUNC Char internal::thousands_sep_impl(locale_ref) {
return FMT_STATIC_THOUSANDS_SEPARATOR; return FMT_STATIC_THOUSANDS_SEPARATOR;
} }
template <typename Char>
FMT_FUNC Char internal::decimal_point_impl(locale_ref) {
return '.';
}
#endif #endif
FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {} FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {}

View File

@ -742,15 +742,21 @@ inline int count_digits(uint32_t n) {
#endif #endif
template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc); template <typename Char> FMT_API Char thousands_sep_impl(locale_ref loc);
template <typename Char> inline Char thousands_sep(locale_ref loc) { template <typename Char> inline Char thousands_sep(locale_ref loc) {
return Char(thousands_sep_impl<char>(loc)); return Char(thousands_sep_impl<char>(loc));
} }
template <> inline wchar_t thousands_sep(locale_ref loc) { template <> inline wchar_t thousands_sep(locale_ref loc) {
return thousands_sep_impl<wchar_t>(loc); return thousands_sep_impl<wchar_t>(loc);
} }
template <typename Char> FMT_API Char decimal_point_impl(locale_ref loc);
template <typename Char> inline Char decimal_point(locale_ref loc) {
return Char(decimal_point_impl<char>(loc));
}
template <> inline wchar_t decimal_point(locale_ref loc) {
return decimal_point_impl<wchar_t>(loc);
}
// Formats a decimal unsigned integer value writing into buffer. // Formats a decimal unsigned integer value writing into buffer.
// add_thousands_sep is called after writing each char to add a thousands // add_thousands_sep is called after writing each char to add a thousands
// separator if necessary. // separator if necessary.
@ -985,13 +991,13 @@ template <typename Char, typename It> It write_exponent(int exp, It it) {
// The number is given as v = digits * pow(10, exp). // The number is given as v = digits * pow(10, exp).
template <typename Char, typename It> template <typename Char, typename It>
It grisu_prettify(const char* digits, int size, int exp, It it, It grisu_prettify(const char* digits, int size, int exp, It it,
gen_digits_params params) { gen_digits_params params, Char decimal_point) {
// pow(10, full_exp - 1) <= v <= pow(10, full_exp). // pow(10, full_exp - 1) <= v <= pow(10, full_exp).
int full_exp = size + exp; int full_exp = size + exp;
if (!params.fixed) { if (!params.fixed) {
// Insert a decimal point after the first digit and add an exponent. // Insert a decimal point after the first digit and add an exponent.
*it++ = static_cast<Char>(*digits); *it++ = static_cast<Char>(*digits);
if (size > 1) *it++ = static_cast<Char>('.'); if (size > 1) *it++ = decimal_point;
exp += size - 1; exp += size - 1;
it = copy_str<Char>(digits + 1, digits + size, it); it = copy_str<Char>(digits + 1, digits + size, it);
if (size < params.num_digits) if (size < params.num_digits)
@ -1005,7 +1011,7 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
it = std::fill_n(it, full_exp - size, static_cast<Char>('0')); it = std::fill_n(it, full_exp - size, static_cast<Char>('0'));
int num_zeros = (std::max)(params.num_digits - full_exp, 1); int num_zeros = (std::max)(params.num_digits - full_exp, 1);
if (params.trailing_zeros) { if (params.trailing_zeros) {
*it++ = static_cast<Char>('.'); *it++ = decimal_point;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (num_zeros > 1000) if (num_zeros > 1000)
throw std::runtime_error("fuzz mode - avoiding excessive cpu use"); throw std::runtime_error("fuzz mode - avoiding excessive cpu use");
@ -1018,10 +1024,10 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
if (!params.trailing_zeros) { if (!params.trailing_zeros) {
// Remove trailing zeros. // Remove trailing zeros.
while (size > full_exp && digits[size - 1] == '0') --size; while (size > full_exp && digits[size - 1] == '0') --size;
if (size != full_exp) *it++ = static_cast<Char>('.'); if (size != full_exp) *it++ = decimal_point;
return copy_str<Char>(digits + full_exp, digits + size, it); return copy_str<Char>(digits + full_exp, digits + size, it);
} }
*it++ = static_cast<Char>('.'); *it++ = decimal_point;
it = copy_str<Char>(digits + full_exp, digits + size, it); it = copy_str<Char>(digits + full_exp, digits + size, it);
if (params.num_digits > size) { if (params.num_digits > size) {
// Add trailing zeros. // Add trailing zeros.
@ -1037,7 +1043,7 @@ It grisu_prettify(const char* digits, int size, int exp, It it,
if (!params.trailing_zeros) if (!params.trailing_zeros)
while (size > 0 && digits[size - 1] == '0') --size; while (size > 0 && digits[size - 1] == '0') --size;
if (num_zeros != 0 || size != 0) { if (num_zeros != 0 || size != 0) {
*it++ = static_cast<Char>('.'); *it++ = decimal_point;
it = std::fill_n(it, num_zeros, static_cast<Char>('0')); it = std::fill_n(it, num_zeros, static_cast<Char>('0'));
it = copy_str<Char>(digits, digits + size, it); it = copy_str<Char>(digits, digits + size, it);
} }
@ -1453,18 +1459,24 @@ template <typename Range> class basic_writer {
char sign_; char sign_;
int exp_; int exp_;
internal::gen_digits_params params_; internal::gen_digits_params params_;
char_type decimal_point_;
public: public:
grisu_writer(char sign, internal::buffer<char>& digits, int exp, grisu_writer(char sign, internal::buffer<char>& digits, int exp,
const internal::gen_digits_params& params) const internal::gen_digits_params& params,
: digits_(digits), sign_(sign), exp_(exp), params_(params) { char_type decimal_point)
: digits_(digits),
sign_(sign),
exp_(exp),
params_(params),
decimal_point_(decimal_point) {
int num_digits = static_cast<int>(digits.size()); int num_digits = static_cast<int>(digits.size());
int full_exp = num_digits + exp - 1; int full_exp = num_digits + exp - 1;
int precision = params.num_digits > 0 ? params.num_digits : 11; int precision = params.num_digits > 0 ? params.num_digits : 11;
params_.fixed |= full_exp >= -4 && full_exp < precision; params_.fixed |= full_exp >= -4 && full_exp < precision;
auto it = internal::grisu_prettify<char>( auto it = internal::grisu_prettify<char>(
digits.data(), num_digits, exp, internal::counting_iterator<char>(), digits.data(), num_digits, exp, internal::counting_iterator<char>(),
params_); params_, '.');
size_ = it.count(); size_ = it.count();
} }
@ -1475,7 +1487,7 @@ template <typename Range> class basic_writer {
if (sign_) *it++ = static_cast<char_type>(sign_); if (sign_) *it++ = static_cast<char_type>(sign_);
int num_digits = static_cast<int>(digits_.size()); int num_digits = static_cast<int>(digits_.size());
it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_, it = internal::grisu_prettify<char_type>(digits_.data(), num_digits, exp_,
it, params_); it, params_, decimal_point_);
} }
}; };
@ -2739,15 +2751,18 @@ void internal::basic_writer<Range>::write_double(T value,
} else if (spec.align() == ALIGN_DEFAULT) { } else if (spec.align() == ALIGN_DEFAULT) {
as.align_ = ALIGN_RIGHT; as.align_ = ALIGN_RIGHT;
} }
// TODO: add thousands separators if handler.use_locale is set char_type decimal_point = handler.use_locale
? internal::decimal_point<char_type>(locale_)
: static_cast<char_type>('.');
if (use_grisu) { if (use_grisu) {
auto params = internal::gen_digits_params(); auto params = internal::gen_digits_params();
params.fixed = handler.fixed; params.fixed = handler.fixed;
params.num_digits = precision; params.num_digits = precision;
params.trailing_zeros = (precision != 0 && (handler.fixed || !spec.type)) || params.trailing_zeros = (precision != 0 && (handler.fixed || !spec.type)) ||
spec.has(HASH_FLAG); spec.has(HASH_FLAG);
write_padded(as, grisu_writer{sign, buffer, exp, params}); write_padded(as, grisu_writer(sign, buffer, exp, params, decimal_point));
} else { } else {
// TODO: set decimal point
write_padded(as, double_writer{sign, buffer}); write_padded(as, double_writer{sign, buffer});
} }
} }

View File

@ -22,6 +22,7 @@ template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API char internal::thousands_sep_impl(locale_ref); template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*); template FMT_API void internal::buffer<char>::append(const char*, const char*);
@ -43,6 +44,7 @@ template FMT_API void internal::sprintf_format(long double,
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*, template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*); const wchar_t*);

View File

@ -1447,10 +1447,6 @@ TEST(FormatterTest, FormatDouble) {
EXPECT_EQ(buffer, format("{:A}", -42.0)); EXPECT_EQ(buffer, format("{:A}", -42.0));
} }
TEST(FormatterTest, FormatDoubleLocale) {
EXPECT_EQ("1.23", format("{:n}", 1.23));
}
TEST(FormatterTest, PrecisionRounding) { TEST(FormatterTest, PrecisionRounding) {
EXPECT_EQ("0", format("{:.0f}", 0.0)); EXPECT_EQ("0", format("{:.0f}", 0.0));
EXPECT_EQ("0", format("{:.0f}", 0.01)); EXPECT_EQ("0", format("{:.0f}", 0.01));

View File

@ -11,9 +11,15 @@
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Char> struct numpunct : std::numpunct<Char> { template <typename Char> struct numpunct : std::numpunct<Char> {
protected: protected:
Char do_decimal_point() const FMT_OVERRIDE { return '?'; }
Char do_thousands_sep() const FMT_OVERRIDE { return '~'; } Char do_thousands_sep() const FMT_OVERRIDE { return '~'; }
}; };
TEST(LocaleTest, DoubleDecimalPoint) {
std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23));
}
TEST(LocaleTest, Format) { TEST(LocaleTest, Format) {
std::locale loc(std::locale(), new numpunct<char>()); std::locale loc(std::locale(), new numpunct<char>());
EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567)); EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));