Due to the default argument promotions when calling variadic functions, float
values are implicitly converted to double
before the function call, and there is no way to pass a float
value to printf
. Since there's no way to pass a float
value to printf
, there's no need for an explicit format specifier for float
values.
Having said that, AntoineL brought up an interesting point in a comment that %lf
(currently used in scanf
to correspond to an argument type double *
) may have once stood for "long float
", which was a type synonym in pre-C89 days, according to page 42 of the C99 rationale. By that logic, it might make sense that %f
was intended to stand for a float
value which has been converted to a double
.
Regarding the hh
and h
length modifiers, %hhu
and %hu
present a well-defined use-case for these format specifiers: You can print the least significant byte of a large unsigned int
or unsigned short
without a cast, for example:
printf("%hhu\n", UINT_MAX); // This will print (unsigned char) UINT_MAX
printf("%hu\n", UINT_MAX); // This will print (unsigned short) UINT_MAX
It isn't particularly well defined what a narrowing conversion from int
to char
or short
will result in, but it is at least implementation-defined, meaning the implementation is required to actually document this decision.
Following the pattern it should have been %hf
.
Following the pattern you've observed, %hf
should convert values outside of the range of float
back to float
. However, that kind of narrowing conversion from double
to float
results in undefined behaviour, and there's no such thing as an unsigned float
. The pattern you see doesn't make sense.
To be formally correct, %lf
does not denote a long double
argument, and if you were to pass a long double
argument you would be invoking undefined behaviour. It is explicit from the documentation that:
l
(ell)
... has no effect on a following a
, A
, e
, E
, f
, F
, g
, or G
conversion specifier.
I'm surprised nobody else has picked up on this? %lf
denotes a double
argument, just like %f
. If you want to print a long double
, use %Lf
(capital ell).
It should henceforth make sense that %lf
for both printf
and scanf
correspond to double
and double *
arguments... %f
is exceptional only because of the default argument promotions, for the reasons mentioned earlier.
... and %Ld
does not mean long
, either. What that means is undefined behaviour.