3

In an article on the w3resource.com, I've seen the following code:

double x, y;
pr1 = sqrt(pr1);
x = (-b + pr1)/(2*a);
y = (-b - pr1)/(2*a);
printf("Root1 = %.5lf\n", x);
printf("Root1 = %.5lf\n", y);

The article says to take three floating-point numbers. But in their solution (this code snippet), they have used the %lf format specifier. Why have they used %lf instead of %f? Can I also use %f instead of %lf for floating-point numbers?

If I use a float keyword instead of double, and use %f, is there any issue? The output is the same for me.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Anik Sen
  • 31
  • 3
  • 2
    Your question should be complete without us having to go look at images. Please include the code in your question. See [Why should I not upload images of code/data/errors when asking a question?](https://meta.stackoverflow.com/q/285551/15168) – Jonathan Leffler Jun 20 '23 at 13:33
  • 4
    It depends. If the context is `printf()`, then `%lf` and `%f` are interchangeable. If the context is `scanf()`, then `%f` is for reading into `float` variables and `%lf` is for reading into `double` variables (and `%Lf` is for reading into `long double` variables), and the difference is crucial. – Jonathan Leffler Jun 20 '23 at 13:34
  • 3
    In `printf()`, `%lf` and `%f` are interchangeble because any variadic arguments of type `float` get converted to `double` by the *default argument promotion* rules that apply to all function parameters whose types are not specified by an in-scope function prototype. The same rules have no effect on arguments of type `float *` and `double *` (they are not converted to a different type), so `%f` and `%lf` need to be differentiated in `scanf()`. – Ian Abbott Jun 20 '23 at 13:52
  • 1
    A floating point number doesn't have to be of type `float`. This 3 types are all for floating point number: `float`, `double` and `long double`. Don't let the name confuse you. When you change from `double` to `float` you may lose some precision. – 12431234123412341234123 Jun 20 '23 at 14:22
  • 1
    @Anik Sen, Tip: Avoid `"%f"` and `"%lf"`. Use `"%g"`. – chux - Reinstate Monica Jun 20 '23 at 16:55

2 Answers2

6

%f has different meaning in printf and scanf family functions:

  • in printf, %f and %lf are equivalent, and both are matched by double. This is because in variadic functions, all float arguments are promoted to double when the function is called, so there is no difference between passing float and double at a language level.
  • in scanf, %f matches a float* and %lf matches a double*, and this distinction is very important. Using the wrong type of pointer will result in undefined behavior.

You can also use cdecl+ to better understand printf and scanf calls. The output is as follows:

printf("Root1 = %.5lf\n", x)
Write "Root1 = "
Write a decimal double with lower-case infinity/NaN symbols
    precision: 5
Write "\n"

ARGUMENTS & EXPECTED TYPES
--------------------------
x (double)

When in doubt, use %f for float and %lf for double, even in printf, where there is no difference between the two. You will introduce pointless inconsistencies between your scanf and printf format strings otherwise.

Note on floating-point numbers

You may have gotten confused by the meaning of float and floating-point number. float, double, long double, and more are floating-point numbers in C. When the article says floating-point number, they could also mean double, _Decimal32, _Float128, etc.

Why doesn't scanf promote float* to double* similar to printf?

This is not possible, because the object in which we're storing the float cannot be used to store a double.

  • At a language level, this would be a strict aliasing violation.
  • At an implementation-level, we would try to store e.g. eight bytes of double in a float which consists of four bytes, which is impossible.
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
2

From the C Standard (7.21.6.1 The fprintf function)

l (ell) Specifies that a following d, i, o, u, x, or X conversion specifier applies to a long int or unsigned long int argument; that a following n conversion specifier applies to a pointer to a long int argument; that a following c conversion specifier applies to a wint_t argument; that a following s conversion specifier applies to a pointer to a wchar_t argument; or has no effect on a following a, A, e, E, f, F, g, or G conversion specifier.

and (6.5.2.2 Function calls)

7 If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

and

6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions.

Using the length modifier l with the conversion specifier f will only confuse readers of the code. Its using will arise questions. The readers of the code will think that either the author of the code does not know that the length modifier has no effect or that without the length modifier a call of printf (or fprintf) will indeed have undefined behavior and using the length modifier is necessary.

As for using the length modifier with a call of scanf then the function deals with pointers of the type float * or double *. That is the default argument promotions are not applied to them. So to distinguish whether a pointer points to an object of the type float or to an object of the type double you have to use the length modifier l with the conversion specifier f with pointers of the type double * or without the length modifier with pointers of the type float *.

Adepts of using the length modifier l with objects of the type double in calls of printf (or fprintf) only make code less maintainable because for example if the type of an outputted variable will be changed from the type double to the type float they will have to find and change all calls printf where the variable is used.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    Obviously, this is very a opinion-based topic, but personally I find it better to use `%lf` for `double` consistently everywhere, even in `printf` where you could be more concise. *Technically*, you don't need to explicitly cast in a lot of places, but doing so conveys your intent clearly. Similarly, you don't need to use `%lf` in `printf`, but it conveys that you *intended* to print a `double`, not a `float`. – Jan Schultke Jun 20 '23 at 15:25
  • @JanSchultke You are right. Low-qualified programmers prefer to write for example int x = ( int ) 'A'; or printf( "%lf\n", 1.0 );.:) – Vlad from Moscow Jun 20 '23 at 15:39
  • @JanSchultke Pay also attention to that the type of the outputted variable can be changed from the type double to the type float. In this case you will need to find and change all calls of printf where the variable is used.:) – Vlad from Moscow Jun 20 '23 at 16:15
  • I don't see any need in being condescending about this and calling them "low-qualified". C's type system is full of surprises and gotchas. The fact that `float` gets promoted to `double` when calling variadic functions isn't intuitive at all. In software development, it's much more important to have consistent rules that everyone will understand and follow, rather than saving *literally one* character to feel smug about your in-depth knowledge of `printf`. And having to update format strings when types of variables change is not limited to `float` and `%f`, it's a general refactoring problem. – Jan Schultke Jun 20 '23 at 20:32
  • @JanSchultke Even if to write a code for beginners (not professional code) then even in this case it is a bad idea to use %lf because beginners will think that it is required according to the C standard to use the length modifier with objects of the type double to avoid undefined behavior.:) Though even in books for beginners I have not encountered an advice to use %lf instead of just %f.:) – Vlad from Moscow Jun 20 '23 at 21:14
  • This isn't about "beginners", this is about collaborating on software projects and making sane style decisions. You can do no harm by using `%lf` when it's not strictly necessary, but you can do very real harm by copying and pasting a format string from `printf` to `scanf` if you're using `double` in your project, but chose to write `%f` for the sake of convenience. In the real world, no one cares whether you're a l33t developer who knows all the tricks for saving *literally one* character in format strings. People care about consistent, easy-to-follow rules that get the job done. – Jan Schultke Jun 20 '23 at 21:26
  • @JanSchultke I agree with you only in one thing that low-qualified programmers can make desicions about using bad styles of programming that only confuse readers of code and arise questions to the code: whether programmers who wrote the code know about default argument promotions and that the length modifier l has no effect.:) – Vlad from Moscow Jun 20 '23 at 21:54