From bc14c6ee20869133a66a9f0f1d05a206e769f1c6 Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Wed, 3 Jul 2019 17:25:57 -0700 Subject: [PATCH] Use the decimal point from locale --- include/fmt/format-inl.h | 8 ++++++++ include/fmt/format.h | 43 +++++++++++++++++++++++++++------------- src/format.cc | 2 ++ test/format-test.cc | 4 ---- test/locale-test.cc | 6 ++++++ 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 6d537f11..c3a38b5f 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -210,12 +210,20 @@ template FMT_FUNC Char thousands_sep_impl(locale_ref loc) { return std::use_facet>(loc.get()) .thousands_sep(); } +template FMT_FUNC Char decimal_point_impl(locale_ref loc) { + return std::use_facet>(loc.get()) + .decimal_point(); +} } // namespace internal #else template FMT_FUNC Char internal::thousands_sep_impl(locale_ref) { return FMT_STATIC_THOUSANDS_SEPARATOR; } +template +FMT_FUNC Char internal::decimal_point_impl(locale_ref) { + return '.'; +} #endif FMT_API FMT_FUNC format_error::~format_error() FMT_NOEXCEPT {} diff --git a/include/fmt/format.h b/include/fmt/format.h index 9299782f..38f791cd 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -742,15 +742,21 @@ inline int count_digits(uint32_t n) { #endif template FMT_API Char thousands_sep_impl(locale_ref loc); - template inline Char thousands_sep(locale_ref loc) { return Char(thousands_sep_impl(loc)); } - template <> inline wchar_t thousands_sep(locale_ref loc) { return thousands_sep_impl(loc); } +template FMT_API Char decimal_point_impl(locale_ref loc); +template inline Char decimal_point(locale_ref loc) { + return Char(decimal_point_impl(loc)); +} +template <> inline wchar_t decimal_point(locale_ref loc) { + return decimal_point_impl(loc); +} + // Formats a decimal unsigned integer value writing into buffer. // add_thousands_sep is called after writing each char to add a thousands // separator if necessary. @@ -985,13 +991,13 @@ template It write_exponent(int exp, It it) { // The number is given as v = digits * pow(10, exp). template 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). int full_exp = size + exp; if (!params.fixed) { // Insert a decimal point after the first digit and add an exponent. *it++ = static_cast(*digits); - if (size > 1) *it++ = static_cast('.'); + if (size > 1) *it++ = decimal_point; exp += size - 1; it = copy_str(digits + 1, digits + size, it); 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('0')); int num_zeros = (std::max)(params.num_digits - full_exp, 1); if (params.trailing_zeros) { - *it++ = static_cast('.'); + *it++ = decimal_point; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (num_zeros > 1000) 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) { // Remove trailing zeros. while (size > full_exp && digits[size - 1] == '0') --size; - if (size != full_exp) *it++ = static_cast('.'); + if (size != full_exp) *it++ = decimal_point; return copy_str(digits + full_exp, digits + size, it); } - *it++ = static_cast('.'); + *it++ = decimal_point; it = copy_str(digits + full_exp, digits + size, it); if (params.num_digits > size) { // Add trailing zeros. @@ -1037,7 +1043,7 @@ It grisu_prettify(const char* digits, int size, int exp, It it, if (!params.trailing_zeros) while (size > 0 && digits[size - 1] == '0') --size; if (num_zeros != 0 || size != 0) { - *it++ = static_cast('.'); + *it++ = decimal_point; it = std::fill_n(it, num_zeros, static_cast('0')); it = copy_str(digits, digits + size, it); } @@ -1453,18 +1459,24 @@ template class basic_writer { char sign_; int exp_; internal::gen_digits_params params_; + char_type decimal_point_; public: grisu_writer(char sign, internal::buffer& digits, int exp, - const internal::gen_digits_params& params) - : digits_(digits), sign_(sign), exp_(exp), params_(params) { + const internal::gen_digits_params& params, + char_type decimal_point) + : digits_(digits), + sign_(sign), + exp_(exp), + params_(params), + decimal_point_(decimal_point) { int num_digits = static_cast(digits.size()); int full_exp = num_digits + exp - 1; int precision = params.num_digits > 0 ? params.num_digits : 11; params_.fixed |= full_exp >= -4 && full_exp < precision; auto it = internal::grisu_prettify( digits.data(), num_digits, exp, internal::counting_iterator(), - params_); + params_, '.'); size_ = it.count(); } @@ -1475,7 +1487,7 @@ template class basic_writer { if (sign_) *it++ = static_cast(sign_); int num_digits = static_cast(digits_.size()); it = internal::grisu_prettify(digits_.data(), num_digits, exp_, - it, params_); + it, params_, decimal_point_); } }; @@ -2739,15 +2751,18 @@ void internal::basic_writer::write_double(T value, } else if (spec.align() == ALIGN_DEFAULT) { as.align_ = ALIGN_RIGHT; } - // TODO: add thousands separators if handler.use_locale is set + char_type decimal_point = handler.use_locale + ? internal::decimal_point(locale_) + : static_cast('.'); if (use_grisu) { auto params = internal::gen_digits_params(); params.fixed = handler.fixed; params.num_digits = precision; params.trailing_zeros = (precision != 0 && (handler.fixed || !spec.type)) || 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 { + // TODO: set decimal point write_padded(as, double_writer{sign, buffer}); } } diff --git a/src/format.cc b/src/format.cc index b60b7e4f..e0c0574d 100644 --- a/src/format.cc +++ b/src/format.cc @@ -22,6 +22,7 @@ template FMT_API std::locale internal::locale_ref::get() const; // Explicit instantiations for char. 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::append(const char*, const char*); @@ -43,6 +44,7 @@ template FMT_API void internal::sprintf_format(long double, // Explicit instantiations for wchar_t. 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::append(const wchar_t*, const wchar_t*); diff --git a/test/format-test.cc b/test/format-test.cc index c3eedcc2..912775db 100644 --- a/test/format-test.cc +++ b/test/format-test.cc @@ -1447,10 +1447,6 @@ TEST(FormatterTest, FormatDouble) { EXPECT_EQ(buffer, format("{:A}", -42.0)); } -TEST(FormatterTest, FormatDoubleLocale) { - EXPECT_EQ("1.23", format("{:n}", 1.23)); -} - TEST(FormatterTest, PrecisionRounding) { EXPECT_EQ("0", format("{:.0f}", 0.0)); EXPECT_EQ("0", format("{:.0f}", 0.01)); diff --git a/test/locale-test.cc b/test/locale-test.cc index 25a68eaf..2e7a9a2a 100644 --- a/test/locale-test.cc +++ b/test/locale-test.cc @@ -11,9 +11,15 @@ #ifndef FMT_STATIC_THOUSANDS_SEPARATOR template struct numpunct : std::numpunct { protected: + Char do_decimal_point() const FMT_OVERRIDE { return '?'; } Char do_thousands_sep() const FMT_OVERRIDE { return '~'; } }; +TEST(LocaleTest, DoubleDecimalPoint) { + std::locale loc(std::locale(), new numpunct()); + EXPECT_EQ("1?23", fmt::format(loc, "{:n}", 1.23)); +} + TEST(LocaleTest, Format) { std::locale loc(std::locale(), new numpunct()); EXPECT_EQ("1,234,567", fmt::format(std::locale(), "{:n}", 1234567));