mirror of
https://github.com/fmtlib/fmt.git
synced 2026-10-29 02:17:28 +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 {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
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)>
|
||||
@ -246,6 +246,19 @@ class printf_arg_formatter : public arg_formatter<Char> {
|
||||
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:
|
||||
printf_arg_formatter(basic_appender<Char> iter, format_specs& s,
|
||||
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
|
||||
// std::is_same instead.
|
||||
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);
|
||||
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));
|
||||
arg = sv;
|
||||
}
|
||||
if (specs.alt() && arg.visit(is_zero_int())) specs.clear_alt();
|
||||
if (specs.fill_unit<Char>() == '0') {
|
||||
if (is_arithmetic_type(arg.type()) && specs.align() != align::left) {
|
||||
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)
|
||||
report_error("invalid format specifier");
|
||||
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;
|
||||
|
||||
|
||||
@ -278,6 +278,40 @@ TEST(printf_test, int_precision) {
|
||||
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) {
|
||||
char buffer[256];
|
||||
safe_sprintf(buffer, "%.3e", 1234.5678);
|
||||
@ -300,6 +334,8 @@ TEST(printf_test, ignore_precision_for_non_numeric_arg) {
|
||||
TEST(printf_test, dynamic_precision) {
|
||||
EXPECT_EQ("00042", 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,
|
||||
"precision is not integer");
|
||||
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) {
|
||||
EXPECT_EQ("00042", 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_THROW_MSG(test_sprintf("%2$.*1$d", 5.0, 42), format_error,
|
||||
"precision is not integer");
|
||||
@ -561,7 +599,7 @@ TEST(printf_test, check_format_string_regression) {
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user