3

Consider the following invocation of printf():

printf("%.-1f", 34.14);

it specifies a negative precision value of -1. Making this call with glibc, we get:

%0.-1f

is this correct? If so, why?

2
  • 2
    You tagged this language-lawyer but did not specify the C version in the question and answered with a quote from C 1999. The documentation for the C tag says the latest version of the C standard is used, currently 9899:2018, unless otherwise specified in the question. When tagging language-lawyer you ought to be precise about which version of the language you are asking about. The wording for this changed between 1999 and 2018. Commented Jul 23, 2024 at 23:44
  • @EricPostpischil: Fair point. I mistakenly assumed that any changes to printf would only be "additive", i.e. new valid syntax, rather than "negative", i.e. making valid syntax invalid. Commented Jul 24, 2024 at 8:28

2 Answers 2

9

The wording changed between C11 and C17/18. In C17/18 it says:

§7.21.6.1 The fprintf function
4. [...] The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional nonnegative decimal integer; [...]

The way I read it, the behavior when using a negative integer is not defined.

This is not a breaking change since it does not make previously valid programs invalid. The intent was never to allow negative values as pointed out in DR220 - Definition of "decimal integer" / N32079:

The term "decimal integer" is defined neither in the Standard nor in ISO 2382-1. Therefore it is not possible to tell whether, in each case:

  • the value may be zero
  • a non-significant leading zero digit may be used
  • the value may be negative.
Suggested Technical Corrigendum

Add a new paragraph to 7.1.1:
[#x] A decimal integer is a sequence of digits which may begin with one or more zeros, but is nonetheless interpreted as decimal, not octal.

Technical Corrigendum

In 7.19.6.1P4, which reads in part: "[...] or by an optional decimal integer [...]"
change "decimal integer" to "non-negative decimal integer".

Sign up to request clarification or add additional context in comments.

5 Comments

This is a good answer, but it complements the discussion of C99. And - if I compile with -std=c99, I still get the same result from glibc.
@einpoklum I spent some time last night looking for the paper in which this word change was suggested in order to be able to provide the original motivation for it - but I couldn't find such a paper. I assume it was made because someone could come to the conclusion you came, but that it has never had defined behavior. None of the major implementations treats it like a 0 in pre C17/18 mode which supports my assumption. I didn't want to put this assumption in the answer though.
@einpoklum I finally found it. They used "non-negative" instead of "nonnegative" in the defect report which is why I didn't find it at first :-)
I think “nonnegative decimal integer” is still a mistake in the standard, because, having implicitly acknowledged a decimal integer could have a minus sign, it also admits there could be a plus sign, but I do not think it was intended to allow and define printf("%.+1f\n", 34.14);. It probably should have said “unsigned decimal integer” or “nonempty sequence of digits”.
@EricPostpischil I had similar thought when I found the defect report. Looking at the first suggested Corrigendum: "A decimal integer is a sequence of digits which may begin with one or more zeros, but is nonetheless interpreted as decimal, not octal" - there is no room for negatives at all - but they still found it necessary to make that explicit.
0

I believe that's an error. the C standard (ISO/IEC 9899:1999) says:

§7.19.6.1

  1. Each conversion specification is introduced by the character %. After the %, the following appear in sequence:

    ... snip ...

    - An optional precision ... The precision takes the form of a period (.) followed either by an asterisk * (described later) or by an optional decimal integer;

... snip ...

  1. As noted above, a field width, or precision, or both, may be indicated by an asterisk. In this case, an int argument supplies the field width or precision. ... A negative precision argument is taken as if the precision were omitted.

Well, -1 is a negative integer; so it can legitimately be specified literally in the format string; and while the text explaining what to do with a negative specified precision appears in the clause regarding asterisk-indicated values - surely it should be the same logic as for literally-specified values (just like it is for widths).

So, I believe the precision value should be ignored in this cause, and the format string is the equivalent of "%f", so the output should be:

34.140000

(default precision of 6)

2 Comments

The precision integer argument in 5 is not part of the string but an argument to the printf function like: printf("%.*f", -1, 34.14)
@OfNothing: Indeed, but clause 5 indicates how interpret a negative precision value.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.