Conversation
vitaut
left a comment
There was a problem hiding this comment.
Thanks for the PR. Please keep the printf-specific logic in fmt/printf.h only.
|
Moved it into printf.h in 1fcee22. format.h is back to what it was, and |
vitaut
left a comment
There was a problem hiding this comment.
alt_zero is computed before the length conversion, so e.g. %#.0hho with 256 misses the octal-zero special case after hh converts the value to 0. Could we move the alt/zero handling after convert_arg and parsing the presentation type instead?
…tion type parsing
|
Updated in d31459c. |
vitaut
left a comment
There was a problem hiding this comment.
Negative dynamic precision is treated as zero by printf_precision_handler, but printf requires a negative * precision to be ignored, as if no precision had been specified.
This becomes observable with the new specs.precision == 0 check. For example:
fmt::sprintf("%.*d", -1, 0)should produce 0, but with this change it produces an empty string. Similarly, %+.*d should produce +0.
Could we preserve the unspecified-precision sentinel for negative dynamic precision instead of clamping it to zero, and add a test for this case?
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::printfprints0instead. This affectsd,i,o,u,xandX.Reproduction
printffmt::sprintf%.0d0%.d0%.0x0%5.0d␣␣␣␣␣␣␣␣␣0%-5.0d␣␣␣␣␣0␣␣␣␣%+.0d++0% .0d␣␣0%#.0x0Found by diffing
fmt::sprintfagainstsnprintfacross the flag, width and precision combinations fordiouxXfFeEgG. A nonzero value, or any precision above zero, was already correct.Changes
write_intnow 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:%+.0dstill yields+and%5.0dstill yields five spaces.%#ois the one exception in C: "if the value and precision are both 0, a single 0 is printed".printf.hclearsaltfor 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
printfby construction. The format API rejects a precision for integer arguments (fmt::format("{:.0}", 0)throwsinvalid format specifier), sowrite_intcan only see a nonnegative precision throughprintf.Testing
Added
printf_test.zero_int_with_zero_precision, covering every affected conversion, the#oand#xcases, the sign, space and width interactions, and unaffected cases such as%.0dwith 42 and%.1dwith 0. Every expected value in it was generated from the platformsnprintfrather than written by hand.EXPECT_PRINTFalso exercises the positional form of each.The test fails on main, producing
0where the empty string is required. All 22 ctest targets pass with the change.Not included
The same sweep showed that
fmt::printfapplies the+and space flags to the unsigned conversionso,u,xandX, where C ignores them, so%+xof 0 gives+0rather than0. 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.