mirror of
https://github.com/fmtlib/fmt.git
synced 2026-11-10 15:20:13 +08:00
Fix printf conversion of a zero value with a precision of zero (#4942)
This commit is contained in:
parent
fd0a9b6620
commit
cfb57bfa3a
@ -88,7 +88,7 @@ struct printf_precision_handler {
|
|||||||
auto operator()(T value) -> int {
|
auto operator()(T value) -> int {
|
||||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||||
report_error("number is too big");
|
report_error("number is too big");
|
||||||
return max_of(static_cast<int>(value), 0);
|
return detail::is_negative(value) ? -1 : static_cast<int>(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
@ -246,6 +246,19 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
detail::write<Char>(this->out, value, this->specs, this->locale);
|
detail::write<Char>(this->out, value, this->specs, this->locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C requires no characters when converting a zero value with a precision of
|
||||||
|
// zero, so only the sign and the padding remain.
|
||||||
|
void write_zero_with_zero_precision() {
|
||||||
|
auto s = this->specs;
|
||||||
|
const char* sign_str = s.sign() == sign::plus ? "+"
|
||||||
|
: s.sign() == sign::space ? " "
|
||||||
|
: "";
|
||||||
|
s.set_type(presentation_type::none);
|
||||||
|
if (s.align() == align::none || s.align() == align::numeric)
|
||||||
|
s.set_align(align::right);
|
||||||
|
write_bytes<Char>(this->out, sign_str, s);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
|
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
|
||||||
context_type& ctx)
|
context_type& ctx)
|
||||||
@ -258,6 +271,13 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
|||||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||||
// std::is_same instead.
|
// std::is_same instead.
|
||||||
if (!std::is_same<T, Char>::value) {
|
if (!std::is_same<T, Char>::value) {
|
||||||
|
auto t = this->specs.type();
|
||||||
|
if (value == 0 && this->specs.precision == 0 &&
|
||||||
|
(t == presentation_type::dec || t == presentation_type::oct ||
|
||||||
|
t == presentation_type::hex)) {
|
||||||
|
write_zero_with_zero_precision();
|
||||||
|
return;
|
||||||
|
}
|
||||||
write(value);
|
write(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -490,7 +510,6 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
||||||
arg = sv;
|
arg = sv;
|
||||||
}
|
}
|
||||||
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
|
|
||||||
if (specs.fill_unit<Char>() == '0') {
|
if (specs.fill_unit<Char>() == '0') {
|
||||||
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
|
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
|
||||||
specs.set_align(align::numeric);
|
specs.set_align(align::numeric);
|
||||||
@ -550,6 +569,13 @@ void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
|||||||
if (specs.type() == presentation_type::none)
|
if (specs.type() == presentation_type::none)
|
||||||
report_error("invalid format specifier");
|
report_error("invalid format specifier");
|
||||||
if (upper) specs.set_upper();
|
if (upper) specs.set_upper();
|
||||||
|
// For '#o', C requires a single '0' when the value and the precision are
|
||||||
|
// both zero.
|
||||||
|
bool alt_zero = specs.alt() && arg.visit(is_zero_int());
|
||||||
|
if (alt_zero) specs.clear_alt();
|
||||||
|
if (alt_zero && specs.type() == presentation_type::oct &&
|
||||||
|
specs.precision == 0)
|
||||||
|
specs.precision = 1;
|
||||||
|
|
||||||
start = it;
|
start = it;
|
||||||
|
|
||||||
|
|||||||
@ -278,6 +278,40 @@ TEST(printf_test, int_precision) {
|
|||||||
EXPECT_PRINTF("00042 ", "%-#10.5o", 042);
|
EXPECT_PRINTF("00042 ", "%-#10.5o", 042);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C99 7.21.6.1: the result of converting a zero value with a precision of zero
|
||||||
|
// is no characters.
|
||||||
|
TEST(printf_test, zero_int_with_zero_precision) {
|
||||||
|
EXPECT_PRINTF("", "%.0d", 0);
|
||||||
|
EXPECT_PRINTF("", "%.d", 0);
|
||||||
|
EXPECT_PRINTF("", "%.0i", 0);
|
||||||
|
EXPECT_PRINTF("", "%.0o", 0);
|
||||||
|
EXPECT_PRINTF("", "%.0u", 0);
|
||||||
|
EXPECT_PRINTF("", "%.0x", 0);
|
||||||
|
EXPECT_PRINTF("", "%.0X", 0);
|
||||||
|
|
||||||
|
// '#' forces a single '0' for octal, but has no effect on other conversions.
|
||||||
|
EXPECT_PRINTF("0", "%#.0o", 0);
|
||||||
|
EXPECT_PRINTF("0", "%#.0hho", 256);
|
||||||
|
EXPECT_PRINTF("", "%#.0x", 0);
|
||||||
|
EXPECT_PRINTF("", "%#.0X", 0);
|
||||||
|
|
||||||
|
// The sign, space and width still apply.
|
||||||
|
EXPECT_PRINTF("+", "%+.0d", 0);
|
||||||
|
EXPECT_PRINTF(" ", "% .0d", 0);
|
||||||
|
EXPECT_PRINTF(" ", "%5.0d", 0);
|
||||||
|
EXPECT_PRINTF(" ", "%-5.0d", 0);
|
||||||
|
EXPECT_PRINTF(" ", "%05.0d", 0);
|
||||||
|
EXPECT_PRINTF(" 0", "%#5.0o", 0);
|
||||||
|
EXPECT_PRINTF("0 ", "%#-5.0o", 0);
|
||||||
|
|
||||||
|
// A nonzero value or a nonzero precision is unaffected.
|
||||||
|
EXPECT_PRINTF("42", "%.0d", 42);
|
||||||
|
EXPECT_PRINTF("-42", "%.0d", -42);
|
||||||
|
EXPECT_PRINTF("ff", "%.0x", 255);
|
||||||
|
EXPECT_PRINTF("0", "%.1d", 0);
|
||||||
|
EXPECT_PRINTF("00", "%.2d", 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(printf_test, float_precision) {
|
TEST(printf_test, float_precision) {
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
safe_sprintf(buffer, "%.3e", 1234.5678);
|
safe_sprintf(buffer, "%.3e", 1234.5678);
|
||||||
@ -300,6 +334,8 @@ TEST(printf_test, ignore_precision_for_non_numeric_arg) {
|
|||||||
TEST(printf_test, dynamic_precision) {
|
TEST(printf_test, dynamic_precision) {
|
||||||
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
|
EXPECT_EQ("00042", test_sprintf("%.*d", 5, 42));
|
||||||
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
|
EXPECT_EQ("42", test_sprintf("%.*d", -5, 42));
|
||||||
|
EXPECT_EQ("0", test_sprintf("%.*d", -1, 0));
|
||||||
|
EXPECT_EQ("+0", test_sprintf("%+.*d", -1, 0));
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%.*d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
|
EXPECT_THROW_MSG(test_sprintf("%.*d"), format_error, "argument not found");
|
||||||
@ -326,6 +362,8 @@ TEST(printf_test, positional_width) {
|
|||||||
TEST(printf_test, positional_precision) {
|
TEST(printf_test, positional_precision) {
|
||||||
EXPECT_EQ("00042", test_sprintf("%2$.*1$d", 5, 42));
|
EXPECT_EQ("00042", test_sprintf("%2$.*1$d", 5, 42));
|
||||||
EXPECT_EQ("42", test_sprintf("%2$.*1$d", -5, 42));
|
EXPECT_EQ("42", test_sprintf("%2$.*1$d", -5, 42));
|
||||||
|
EXPECT_EQ("0", test_sprintf("%2$.*1$d", -1, 0));
|
||||||
|
EXPECT_EQ("+0", test_sprintf("%2$+.*1$d", -1, 0));
|
||||||
EXPECT_EQ("Hell", test_sprintf("%2$.*1$s", 4, "Hello"));
|
EXPECT_EQ("Hell", test_sprintf("%2$.*1$s", 4, "Hello"));
|
||||||
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error,
|
EXPECT_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error,
|
||||||
"precision is not integer");
|
"precision is not integer");
|
||||||
@ -561,7 +599,7 @@ TEST(printf_test, check_format_string_regression) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(printf_test, fixed_large_exponent) {
|
TEST(printf_test, fixed_large_exponent) {
|
||||||
EXPECT_EQ("1000000000000000000000", fmt::sprintf("%.*f", -13, 1e21));
|
EXPECT_EQ("1000000000000000000000.000000", fmt::sprintf("%.*f", -13, 1e21));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(printf_test, make_printf_args) {
|
TEST(printf_test, make_printf_args) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user