mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-28 18:32:46 +00:00
Use the decimal point from locale
This commit is contained in:
parent
476f25cd81
commit
bc14c6ee20
@ -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 {}
|
||||||
|
@ -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});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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*);
|
||||||
|
@ -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));
|
||||||
|
@ -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));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user