Printing the result of operations on integers and floats - c

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.

Related

difference between %fl and %lf in C

I am currently learning about C language data type and I try to print a double variable the compiler suggested me to use fl after I type '%', and I got 1 number in the end of precision decimal line. Compare to %lf it will print six precision decimals in total
double num=12322;
printf("%lf",num);// result is 12322.000000
printf("%fl",num);// result is 12322.0000001
I've searched plenty of place but mostly the difference between %f and %lf is frequently asked. Is my situation could possiply the same?
In this call of printf
printf("%lf",num);// result is 12322.000000
the length modifier l in the conversion specifier %lf has no effect.
From the C Standard (7.21.6.1 The fprintf function)
7 The length modifiers and their meanings are:
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.
In this call of printf
printf("%fl",num);// result is 12322.0000001
where in the comment there shall be written the letter 'l' instead of the number 1 as you think
// result is 12322.000000l
^^^
the format string "%fl" means that after outputting an object of the type double due to the conversion specification %f there will be outputted the letter 'l'.
Pay attention to that with the conversion specifier f there can be used one more letter 'l' that is the upper case letter 'L'. In this case the conversion specification %Lf serves to output objects of the type long double.
I think you actually have a typo in your output....
double num=12322;
printf("%lf",num);// result is 12322.000000
printf("%fl",num);// result is 12322.0000001
is actually
double num=12322;
printf("%lf",num);// result is 12322.000000
printf("%fl",num);// result is 12322.000000l
The C standard says that the float is converted to a double when passed to a variadic function, so %lf and %f are equivalent; %fl is the same a %f... with an l after it.
There are two correct ways of printing a value of type double:
printf("%f", num);
or
printf("%lf", num);
These two have exactly the same effect. In this case, the "l" modifier is effectively ignored.
The reason they have the same effect is that printf is special. printf accepts a variable number of arguments, and for such functions, C always applies the default argument promotions. This means that all integer types smaller than int are promoted to int, and float is promoted to double. So if you write
float f = 1.5;
printf("%f\n", f);
then f is promoted to double before being passed. So inside printf, it always gets a double. So %f will never see a float, always a double. So %f is written to expect a double, so it ends up working for both float and double. So you don't need a l modifier to say which. But that's kind of confusing, so the Standard says you can put the l there if you want to — but you don't have to, and it doesn't do anything.
(This is all very different, by the way, from scanf, where %f and %lf are totally different, and must be explicitly matched to arguments of type float * versus double *.)
I have no idea why your IDE complained about (put a red line under) %lf, and I have no idea what it meant by suggesting, as you said,
fl, lg, l, f,
elf, Alf, ls, sf,
if, la, lo, of
Some of those look they might be nonstandard, system-specific extensions, but some (especially fl) are nonsense. So, bottom line, it sounds like your IDE's suggestion was confusing, unnecessary, and quite possibly wrong.

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.

Conversion type in printf (with no casting operator)

I'm trying to run the following code for a demonstration of how conversion works.
#include <stdio.h>
int main()
{
int x=5;
int y=2;
float z;
printf("x=%d\n", x);
printf("y=%d\n", y);
//Doing division as it is
z= x/y;
printf("With no preliminar assignment: (z=x/y)\n");
printf("\"z\" is: %d (integer) or %lf (double) or %f(float)\n\n\n", z, z, z);
//Doing assignment before division
printf("With the assignment: (z=x)\n");
z=x;
printf("\"z\" before division is:");
printf("%d (integer) or %lf (double) or %f(float)\n", z, z, z);
//Doing division after assignment
z/=y;
printf("\"z\" after division with assignment: (z=z/y)");
printf(" is: %d (integer) or %lf (double) or %f(float)\n", z, z, z);
return 0;
}
The OUTPUT is:
x=5
y=2
With no preliminar assignment: (z=x/y)
"z" is: 0 (integer) or 0.000000 (double) or 0.000000(float)
With the assignment: (z=x)
"z" before division is:0 (integer) or 0.000000 (double) or 0.000000(float)
"z" after division with assignment: (z=z/y) is: 0 (integer) or 0.000000 (double) or 0.000000(float)
This leave me with a doubt, also because my professor at University wrote the following code:
int x=5;
int y=2;
float z;
int main (void) {
z = x / y;
return 0;
}
And for him, z will assume 2.0 (Which is logically correct, but not in printf).
So my final question is, why am I having 0 as result in all the printf?
(I would like to not to use type casting operator)
printf(), as many variadic functions, use a format string to tell to the function which kind of parameters expect on the call stack, but this doesn't imply that any conversion is actuated on such parameters based on the format string.
Anyway any decent compiler will warn you about the format/parameters type mismatch, this means that you deliberately ignored them..
Probably you are making a confusion between argument promotions and format specifier.
In variable arguments functions the parameters not explicitly defined, those identified by ..., are handled in a standard way as described in C standard ISO/IEC 9899:201x, §6.5.2.2 Function calls. In subparagraph 6 we read:
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.
If the
number of arguments does not equal the number of parameters, the
behavior is undefined. If the function is defined with a type that
includes a prototype, and either the prototype ends with an ellipsis
(, ...) or the types of the arguments after promotion are not
compatible with the types of the parameters, the behavior is
undefined. If the function is defined with a type that does not
include a prototype, and the types of the arguments after promotion
are not compatible with those of the parameters after promotion, the
behavior is undefined, except for the following cases:
one promoted
type is a signed integer type, the other promoted type is the
corresponding unsigned integer type, and the value is representable in
both types;
both types are pointers to qualified or unqualified > versions of a character type or void.
So if you want have a meaningful output you must explicetely convert parameters as in:
printf(" is: %d (integer) or %lf (double) or %f(float)\n", (int)z, z, z);
Note that only the cast to int is due, leaving the other 2 entries to default promotion that will convert float to double satisfying what the function expect based on the format string. Without the explicit cast the compiler will promote the variable z from float to double, as per the default argument promotion rules, while the printf() code will try to access it as an int.
The problem of values all zero in your code is due to the mismatch in the parameters size int/double that broken the stack unwrap.
why am I having 0 as result in all the printf?
You have an incomplete understanding of the significance of formatting directives in a printf format string. Above all else they convey to printf the data type of their corresponding arguments, which are not otherwise known to printf. They also specify details of how to format the value, but that is secondary to the question of how to interpret the argument list. If the directives do not correspond correctly to the actual argument types, then the resulting behavior is wholly undefined. Certainly printf can produce surprising output in such cases,* but in principle, anything could happen.
Thus a statement such as this ...
printf("\"z\" is: %d (integer) or %lf (double) or %f(float)\n\n\n", z, z, z);
... where conversion directives specify different, incompatible types for the same value, is always incorrect. One way to correct it would be to convert the arguments to the correct types, like so:
printf("\"z\" is: %d (integer) or %lf (double) or %f(float)\n\n\n", (int) z, (double) z, z);
But that's not particularly useful for your apparent purpose. It appears that all the conversions you're actually interested in happen before or during each assignment to z, so nothing is gained by considering the results of afterward converting that result to other data types. This, then, would be a better correction:
printf("\"z\" is: %f(float)\n\n\n", z);
Alternatively, to separate the effects of conversion during assignment from the effects of conversion during arithmetic computation, you could perform different assignments to variables of the different types of interest. For example,
int x = 5;
int y = 2;
int zi;
double zd;
float zf;
// ...
zi = x / y;
zd = x / y;
zf = x / y;
// ...
printf("\"z\" is: %d (integer) or %lf (double) or %f(float)\n\n\n", zi, zd, zf);
* This is the answer to the actual question posed.

Unexpected results when using printf

I am seeing different results for two program that I expect to produce the same output, the first case:
int money;
printf("Enter the price of the car: ");
scanf("%d", &money);
printf("\nResult: %d .\n", money+money*0.4);
the second case:
int money;
printf("Enter the price of the car: ");
scanf("%d", &money);
money=money+money*0.4;
printf("\nResult: %d .\n", money );
return 0;
In the first case the result of the printf is 0 but not in the second case. Why am I seeing these different results?
the %d format specifier tells printf that you are passing in an int but in the first case you are passing in a double which is also undefined behavior and so the results are not reliable. The result of:
money+money*0.4
is double since a floating constant is double unless it has a suffix such as f and the results of the multiplication and addition are double as well due to the usual arithmetic conversions which both operations are subject to and which will cause the value of money to be converted to double for the operation.
In the second case you are correctly passing in int and since you are assigning the result to money:
money=money+money*0.4
it will truncate the double value. I am not sure what compiler you are using but both clang and gcc without any warning flags both warn about the incorrect format specifier, gcc for example says:
warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat]
So if you are not seeing any warning for that line you should look into setting your warning levels higher.
For completeness sake the draft C99 standard section 7.19.6.1 The fprintf function, which also covers printf with respect to the format specifiers, In paragraph 9 says:
If a conversion specification is invalid, the behavior is undefined.248) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
Check the multiplication in line 7.
You could change the last lines to:
float price = money * 1.4;
printf( "\nResult %f.\n", price);
money+money*0.4 will implicitly cast money to double and thus make the %d a wrong format specifier for that value.

default argument promotion

main(){
printf("%d %d",1234.5);
}
output: 0 1083394560
This is the case of default argument promotion where float variable is treated as double and then first %d displays 0 and other one displays the value of rest 4 bytes i.e. 1083394560.
My question is if %d reads first 4 bytes then how does this program
main()
{
printf("%f",1234.5);
}
gives the right output. Because float is also of 4 bytes, it should also behave like %d.
Huh? %f can "know" that a float passed as a vararg argument has been promoted to something bigger (double) and act accordingly, of course. The code for %d does not know that it should expect a promoted floating point value; it expects a (suitably promoted) int.
The %f format string takes a double value in the argument list, and prints it out. Since un-suffixed floating point literals are doubles in C, and that doubles are not promoted or otherwise changed by default promotion rules, there is no magic at all happening with your second example.
Your first example is undefined behavior, anything could happen.
C99 §7.19.6.1 The fprintf function
If there are insufficient arguments for the format, the behavior is
undefined.
[...]
If any argument is
not the correct type for the corresponding conversion specification, the behavior is
undefined.
You answer your question by yourself:
This is the case of default argument promotion where float variable is treated as double
Of course, %f knows about this default argument promotion and only deals with doubles, not with floats.

Resources