Skip to content

Fix printf conversion of a zero value with a precision of zero - #4942

Open
sachhg wants to merge 1 commit into
fmtlib:mainfrom
sachhg:fix/printf-zero-value-zero-precision
Open

Fix printf conversion of a zero value with a precision of zero#4942
sachhg wants to merge 1 commit into
fmtlib:mainfrom
sachhg:fix/printf-zero-value-zero-precision

Conversation

@sachhg

@sachhg sachhg commented Sep 10, 2026

Copy link
Copy Markdown

Summary

C requires that converting a zero value with a precision of zero produces no characters (C99 7.21.6.1p8, "The result of converting a zero value with a precision of zero is no characters"). fmt::printf prints 0 instead. This affects d, i, o, u, x and X.

Reproduction

format value C printf fmt::sprintf
%.0d 0 `` 0
%.d 0 `` 0
%.0x 0 `` 0
%5.0d 0 ␣␣␣␣␣ ␣␣␣␣0
%-5.0d 0 ␣␣␣␣␣ 0␣␣␣␣
%+.0d 0 + +0
% .0d 0 ␣0
%#.0x 0 `` 0

Found by diffing fmt::sprintf against snprintf across the flag, width and precision combinations for diouxXfFeEgG. A nonzero value, or any precision above zero, was already correct.

Changes

write_int now emits no digits when the precision is zero and the value is zero. The prefix, the sign and the width padding are unaffected, which is what C requires: %+.0d still yields + and %5.0d still yields five spaces.

%#o is the one exception in C: "if the value and precision are both 0, a single 0 is printed". printf.h clears alt for a zero value before the conversion specifier has been parsed, so it now remembers that case and raises the precision to 1 for octal. That mirrors how the standard states the rule, as # increasing the precision when necessary to force a leading zero.

This is scoped to printf by construction. The format API rejects a precision for integer arguments (fmt::format("{:.0}", 0) throws invalid format specifier), so write_int can only see a nonnegative precision through printf.

Testing

Added printf_test.zero_int_with_zero_precision, covering every affected conversion, the #o and #x cases, the sign, space and width interactions, and unaffected cases such as %.0d with 42 and %.1d with 0. Every expected value in it was generated from the platform snprintf rather than written by hand. EXPECT_PRINTF also exercises the positional form of each.

The test fails on main, producing 0 where the empty string is required. All 22 ctest targets pass with the change.

Not included

The same sweep showed that fmt::printf applies the + and space flags to the unsigned conversions o, u, x and X, where C ignores them, so %+x of 0 gives +0 rather than 0. C calls those flags undefined for conversions other than signed ones, so it is a compatibility difference rather than a conformance bug, and it seemed better kept out of this change. Happy to open a separate PR if you would like it matched to the common implementations.

@sachhg
sachhg requested a review from vitaut as a code owner September 10, 2026 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant