Due to promotions, by default when calling variative functions, float values ​​are implicitly converted to double before the function is called, and it is not possible to pass float the printf value . Since there is no way to pass the float value to printf , there is no need for an explicit format specifier for the float values.
Having said that AntoineL raised an interesting point in the comment that %lf (currently used in scanf to match the type of the double * argument) might have once stood for the " long float ", which was a type synonym in pre-C89 days, according to page 42 justification C99 . By this logic, it might make sense that %f should have stood for the float value that was converted to double .
As for the length modifiers hh and h , %hhu and %hu represent a well-defined precedent for these format specifiers: you can print the least significant byte of a large unsigned int or unsigned short without casting, 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 is not particularly clearly defined, which will lead to a narrowing of the conversion from int to char or short , but it is at least determined by the implementation, which means that the implementation is necessary to document this decision.
Following the pattern, it should have been %hf .
Following the pattern you observed, %hf should convert the values ​​outside the range of float back to float . However, such a narrowing of the conversion from double to float leads to undefined behavior , and there is no such thing as unsigned float . The pattern you see does not make sense.
To formally be correct, %lf does not denote the long double argument, and if you must pass the long double argument you would invoke undefined behavior . From the documentation it is clear that:
l (ell) ... does not affect the following a , a , e , e , f , f , g or g conversion specifier.
I am surprised that no one took it upon themselves? %lf denotes the double argument, as %lf %f . If you want to print a long double , use %lf (capital ell).
From now on, it should be borne in mind that %lf for printf and scanf correspond to the arguments double and double * ... %f is exclusive only because of the promo arguments by default, for the reasons mentioned earlier.
... and %Ld does not mean long . This means undefined behavior .