Which placeholder should I use in and why? - c

Why the code only works when I use the %lf or %f place holder (second placeholder), but its printing 0 when I use %d?
#include <stdio.h>
void main()
{
long id;
id = 123456789;
double Hourly;
Hourly = 30;
int HoursAday, daysAweek, Fired, Hired;
HoursAday = 8; daysAweek = 5; Fired = 2021; Hired = 2019;
printf("bob, id: \"%d\" should get %lf", id, (Hourly * HoursAday * daysAweek) * (Fired - Hired));

The type of (Hourly * HoursAday * daysAweek) * (Fired - Hired) is double so the correct specifier is %lf. Because of variadic arguments default conversions the code will also work with %f (*). But %d is undefined behavior.
Also, the correct specifier for id (which is of type long) is %ld. %d is undefined behavior.
See the documentation for printf: https://en.cppreference.com/w/c/io/fprintf
(*) A float variadic argument always gets converted to double. So the printf family of functions will never receive a float, they will always receive a double. That's why %f and %lf are for practical reasons equivalent.

This expression
(Hourly * HoursAday * daysAweek) * (Fired - Hired)
has the type double due to the usual arithmetic conversions because the variable Hourly has the type double. That is if one operand has the type double and other operand has an integer type in a binary operation then the operand with the integer type is converted to the type double.
Using the wrong conversion specifier %d designed to output objects of the type int with an object of the type double invokes undefined behavior.
Pay attention to that in this conversion specifier %lf the length modifier l has no effect. So it is enough to use %f.

Related

Why format specifier %d or %i does not work for a double variable but %f works and print a float output of an integer variable?

Must we always use %f to printf a %d value, and %lf to take input by scanf ? Why format specifier %d or %i does not work for a double variable but %f works and print a float output of an integer variable?
Is it always safe to declare double and take input scanf with %lf and printf as %f?
My codes:
int main(void)
{
double dnum = 7777;
printf(" %f \n", dnum);
return 0;
}
output is 7777.000000
int main(void)
{
double dnum = 7777;
printf(" %i \n", dnum);
return 0;
}
Output is 0
int main(void)
{
double dnum = 7777;
printf(" %d \n", dnum);
return 0;
}
Output is 0
Format and data mismatch in printf() invokes undefine behavior.
In your code, the type of dnum is double regardless of its actual value, which may be an integer.
%f can be used to print double, but neithor %d nor %i cannot be used.
If you want to print double, you should use %g, %f or %e depending on the format that you want it to be printed.
%d is the same as %i (for printf) and it goes along with signed integer.
The format specifier: %d or %i expects the argument of signed int, if anything else is given in the formatted printf() statement, such as:
float f = 1.50235F;
printf("%d", f);
Will expect for signed int, if you pass a float or a double instead, it'll tend to undefined behavior and probably print 0.
In a more practical sense, if you want to do-it-yourself, you may add -Wformat flag and run the command in your command prompt or any command shell:
$ gcc -o main main.cpp -Wformat
Then you'll get a warning generated by the compiler similar to the following:
main.c: In function 'int main()':
main.c:9:14: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat=]
9 | printf("%d\n", d);
| ~^ ~
| | |
| int double
| %f
The double has the twice precision than a float could hold (i.e. double has 15 decimal digits of precision, while float has only 7. So, the %f type specifier could be used for double in this case too.)
To know further about the format specifiers: List of all format specifiers in C.
All conversion specifiers expect their corresponding argument to be a specific type; you can't arbitrarily mix and match them. %d and %i expect their corresponding argument to have type int - if it doesn't, the behavior is undefined and you'll (usually) get weird output. Integers and floating point values have very different binary representations and may have different sizes - most modern platforms use 32 bits to store integer values and 64 bits to store doubles, which affects how those values are interpreted in the printf code.
For an authoritative list of conversion specifiers and the types of arguments they take, refer to the C 2011 Online Draft, section 7.21.6.1 (The fprintf function).
Welcome in the world of undefined behavior!
To use the %d conversion specifier to print a double value or the %f conversion specifier to print an int value invokes undefined behavior.
%f is furthermore meant to print a value of type double, not float. A passed float gets automatically promoted.
The C standard states:
If a conversion specification is invalid, the behavior is undefined.288) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
Source: C18, 7.21.6.1/9
d,i - The int argument is converted to signed decimal in the style[-]dddd. The precision specifies the minimum number of digits to appear; if the value being converted can be represented in fewer digits, it is expanded with leading zeros. The default precision is 1. The result of converting a zero value with a precision of zero is no characters.
......
f,F - A double argument representing a floating-point number is converted to decimal notation in the style[-]ddd.ddd, where the number of digits after the decimal-point character is equal to the precision specification. If the precision is missing, it is taken as 6; if the precision is zero and the flag is not specified, no decimal-point character appears. If a decimal-point character appears, at least one digit appears before it. The value is rounded to the appropriate number of digits.
A double argument representing an infinity is converted in one of the styles[-]infor[-]infinity— which style is implementation-defined. A double argument representing a NaN is converted in one of the styles[-]nanor[-]nan(n-char-sequence)— which style, and the meaning of any n-char-sequence, is implementation-defined. The F conversion specifier produces INF,INFINITY, or NAN instead of inf, infinity, or nan, respectively.283)
Source: C18, §7.21.6.1/8
Summary:
Except for the first example, The output you get is any arbitrary value (unless the implementation didn't specified what happens else). It doesn't "work" in the one way nor in the other.

Printing the result of operations on integers and floats

Consider the following C-program:
int main() {
int a =2;
float b = 2;
float c = 3;
int d = 3;
printf("%d %f %d %f %d %f %d %f\n", a/c, a/c, a/d, a/d, b/c, b/c, b/d, b/d);
printf("%d\n", a/c);
}
The output of this is:
0 0.666667 0 0.666667 2 0.666667 0 0.666667
539648
I can't make sense of this at all. Why does printing a/c as an integer give 0, while b/c gives 2? Aren't all integers promoted to floats in computations involving both floats and integers? So the answer should be 0 in both cases.
In the second line of the output I'm simply printing a/c as an integer, which gives a garbage value for some reason (even though it gives 0 when I print it in the first compound printf statement). Why is this happening?
You have undefined behaviour:
printf("%d %f %d %f %d %f %d %f\n", a/c, a/c, a/d, a/d, b/c, b/c, b/d, b/d);
The format specifier for printf must match the type of the provided parameter. As printf doesn't provide a parameter list with types, but only ... there is no implicit type conversion apart from standard type conversion.
If you have UB, basically anything can happen.
What is likely to happen is the following:
Depending on the format specifier, printf consumes a certain number of bytes from the calling parameters. This number of bytes matches the specified format type. If the number of bytes does not match the number of bytes passed as an argument, you are out of sync for all successive parameters.
And of course you do incorrect interpretation of the data.
For starters according to the C Standard the function main without parameters shall be declared like
int main( void )
If you have for example the following declarations
int a =2;
float c = 3;
and then call the function printf the following way
printf( "%d", a / c );
then behind the hood the following events occur.
The expression a / c has the type float due to the usual arithmetic conversions.
As the function printf is declared with the ellipsis notation then to the expression a / c of the type float there are applied the default argument promotions that convert the expression to the type double.
As result in this call there is an attempt to output an expression of the type double using conversion specifier %d designed for the type int. Hanse the call has undefined behavior.
From the C Standard (7.21.6.1 The fprintf function)
9 If a conversion specification is invalid, the behavior is
undefined.275) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.

Meaning of "%lf" place holder

Here is my small program where I intently put the place holder %lf in the second printf. Why the second printf has the same result as the first printf( both printf 1.3).
int main()
{
double f = 1.3;
long l = 1024L;
printf("f = %lf", f);
printf("l = %lf", l);
return 0;
}
It's Undefined behaviour if printf() has format specifier mismatch. %lf expects a double but you are passing a long int.
C11, 7.21.6.1 The fprintf function
9 If a conversion specification is invalid, the behavior is
undefined.282) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.
That said, what probably happens is that when you call printf() the first time, the value of f is passed in a floating point register or at a location in stack for double. The next time you call printf(), it reads from the same location due to the format specifier %lf. As opposed to reading from where the value of l is stored. If you swap the order of printf() calls, you would probably observe a different output. But this is all platform specific. Once your program invokes undefined behaviour, anything can happen. Basically, you can't expect it to do anything sensible and there is absolutely no guarantee about its behaviour.
Here if you change your code to this:
#include <stdio.h>
int main()
{
double f = 1.3;
long l = 1024L;
printf("f = %lf", f);
printf("l = %lf", (float)l);
return 0;
}
you will see that the output would be different. When you pass a long to be presented as double you should expect undefined behavior
You have a specifier mismatch. The value
long l = 1024L;
is interpreted as a double; and this happens to be approximately 1.3 (at least on your and my pc. This might be different on different architectures I think; depending on how long a "long" and a "double" are, and how they are represented internally.
As for the meaning of the %lf placeholder, you can see in the printf documentation that %f means: decimal floating point. the l length modifier has no influence on the %f specifier.
Conclusion: %lf = %f = decimal floating point

Scanf/Printf double variable C

Let's say I have this following bit of code in C:
double var;
scanf("%lf", &var);
printf("%lf", var);
printf("%f", var);
It reads from stdin variable 'var' and then prints twice in stdout 'var'.
I understand that's how you read a double variable from stdin, but my questions are:
Why can you print a double with %lf?
Why can you print a double with %f?
Which one is better and correct to use?
For variable argument functions like printf and scanf, the arguments are promoted, for example, any smaller integer types are promoted to int, float is promoted to double.
scanf takes parameters of pointers, so the promotion rule takes no effect. It must use %f for float* and %lf for double*.
printf will never see a float argument, float is always promoted to double. The format specifier is %f. But C99 also says %lf is the same as %f in printf:
C99 §7.19.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.
When a float is passed to printf, it is automatically converted to a double. This is part of the default argument promotions, which apply to functions that have a variable parameter list (containing ...), largely for historical reasons. Therefore, the “natural” specifier for a float, %f, must work with a double argument. So the %f and %lf specifiers for printf are the same; they both take a double value.
When scanf is called, pointers are passed, not direct values. A pointer to float is not converted to a pointer to double (this could not work since the pointed-to object cannot change when you change the pointer type). So, for scanf, the argument for %f must be a pointer to float, and the argument for %lf must be a pointer to double.
As far as I read manual pages, scanf says that 'l' length modifier indicates (in case of floating points) that the argument is of type double rather than of type float, so you can have 'lf, le, lg'.
As for printing, officially, the manual says that 'l' applies only to integer types. So it might be not supported on some systems or by some standards. For instance, I get the following error message when compiling with gcc -Wall -Wextra -pedantic
a.c:6:1: warning: ISO C90 does not support the ‘%lf’ gnu_printf format [-Wformat=]
So you may want to doublecheck if your standard supports the syntax.
To conclude, I would say that you read with '%lf' and you print with '%f'.

Arithmetic expression as printf arg

I am not able to understand why
float x = (4+2%-8);
printf("%f\n", x);
prints 6.000000 and
printf("%f\n", (4+2%-8));
prints 0.000000. Any information will be helpful.
Regards.
The expression (4 + 2 % -8) produces an integer and you are trying to print a float (and they don't match).
In the first case the integer is converted to float (because of the assignment) so later on the printf works because the value is in a format %f expects.
Try this:
printf("%f\n", (4.0 + 2 % -8));
^
It's because here:
float x = (4+2%-8);
The (4+2%-8) is of type int but is converted to float because that's the type of x. However, here:
printf("%f\n", (4+2%-8));
No cast is performed so you pass an int where it expects a float giving you a garbage value. You can fix this with a simple cast:
printf("%f\n", (float)(4+2%-8));
In the first snippet the resulting int value is implicitly converted to float due to assignment, in the second snippet you are lying to the compiler: you tell it to expect a value of type double but "send" a value of type int instead.
Do not lie to the compiler. It will get its revenge.
Note that the printf conversion specifier "%f" expects a value of type double, but "sending" a float is ok because that value is automagically converted to double before the function is called.

Resources