Unexpected results when using printf - c

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.

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.

Why is this code printing 0?

void main()
{
clrscr();
float f = 3.3;
/* In printf() I intentionaly put %d format specifier to see
what type of output I may get */
printf("value of variable a is: %d", f);
getch();
}
In effect, %d tells printf to look in a certain place for an integer argument. But you passed a float argument, which is put in a different place. The C standard does not specify what happens when you do this. In this case, it may be there was a zero in the place printf looked for an integer argument, so it printed “0”. In other circumstances, something different may happen.
Using an invalid format specifier to printf invokes undefined behavior. This is specified in section 7.21.6.1p9 of the C standard:
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.
What this means is that you can't reliably predict what the output of the program will be. For example, the same code on my system prints -1554224520 as the value.
As to what's most likely happening, the %d format specifier is looking for an int as a parameter. Assuming that an int is passed on the stack and that an int is 4 bytes long, the printf function looks at the next 4 bytes on the stack for the value given. Many implementations don't pass floating point values on the stack but in registers instead, so it instead sees whatever garbage values happen to be there. Even if a float is passed on the stack, a float and an int have very different representations, so printing the bytes of a float as an int will most likely not give you the same value.
Let's look at a different example for a moment. Suppose I write
#include <string.h>
char buf[10];
float f = 3.3;
memset(buf, 'x', f);
The third argument to memset is supposed to be an integer (actually a value of type size_t) telling memset how many characters of buf to set to 'x'. But I passed a float value instead. What happens? Well, the compiler knows that the third argument is supposed to be an integer, so it automatically performs the appropriate conversion, and the code ends up setting the first three (three point zero) characters of buf to 'x'.
(Significantly, the way the compiler knew that the third argument of memset was supposed to be an integer was based on the prototype function declaration for memset which is part of the header <string.h>.)
Now, when you called
printf("value of variable f is: %d", f);
you might think the same thing happens. You passed a float, but %d expects an int, so an automatic conversion will happen, right?
Wrong. Let me say that again: Wrong.
The perhaps surprising fact is, printf is different. printf is special. The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be, because it depends on the details of the %-specifiers buried in the format string. So there are no automatic conversions to just the right type. It's your job to make sure that the types of the arguments you actually pass are exactly right for the format specifiers. If they don't match, the compiler does not automatically perform corresponding conversions. If they don't match, what happens is that you get crazy, wrong results.
(What does the prototype function declaration for printf look like? It literally looks like this: extern int printf(const char *, ...);. Those three dots ... indicate a variable-length argument list or "varargs", they tell the compiler it can't know how many more arguments there are, or what their types are supposed to be. So the compiler performs a few basic conversions -- such as upconverting types char and short int to int, and float to double -- and leaves it at that.)
I said "The compiler can't necessarily know what the right types of the arguments passed to printf are supposed to be", but these days, good compilers go the extra mile and try to figure it out anyway, if they can. They still won't perform automatic conversions (they're not really allowed to, by the rules of the language), but they can at least warn you. For example, I tried your code under two different compilers. Both said something along the lines of warning: format specifies type 'int' but the argument has type 'float'. If your compiler isn't giving you warnings like these, I encourage you to find out if those warnings can be enabled, or consider switching to a better compiler.
Try
printf("... %f",f);
That's how you print float numbers.
Maybe you only want to print x digits of f, eg.:
printf("... %.3f" f);
That will print your float number with 3 digits after the dot.
Please read through this list:
%c - Character
%d or %i - Signed decimal integer
%e - Scientific notation (mantissa/exponent) using e character
%E - Scientific notation (mantissa/exponent) using E character
%f - Decimal floating point
%g - Uses the shorter of %e or %f
%G - Uses the shorter of %E or %f
%o - Signed octal
%s - String of characters
%u - Unsigned decimal integer
%x - Unsigned hexadecimal integer
%X - Unsigned hexadecimal integer (capital letters)
%p - Pointer address
%n - Nothing printed
The code is printing a 0, because you are using the format tag %d, which represents Signed decimal integer (http://devdocs.io).
Could you please try
void main() {
clrscr();
float f=3.3;
/* In printf() I intentionaly put %d format specifier to see what type of output I may get */
printf("value of variable a is: %f",f);
getch();
}

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.

Variable of type double not working when read by scanf()

#include <stdio.h>
int main(){
double d;
scanf("%f", &d);
printf("%f\n\n", d);
system("pause");
return 0;
}
This is what I get:
error.
This code is meant to read the variable double and display it on the screen but it does only display "0.0...".
If I just change the variable type to float it does exactly what I want but if I make it a double it just reads '0'. Why?
In your code,
scanf("%f", &d);
is wrong. For scanf(), %f expects a pointer to float as argument.
Quoting C11, chapter §7.21.6.2, fscanf()
a,e,f,g
Matches an optionally signed floating-point number, infinity, or NaN, whose
format is the same as expected for the subject sequence of the strtod
function. The corresponding argument shall be a pointer to floating.
In case you want to scan a double, you have to use
scanf("%lf", &d);
FWIW, passing incompatible type of argument for any format specifier invokes undefined behavior.
Quoting C11,
[...] Unless assignment suppression was indicated by a *, the
result of the conversion is placed in the object pointed to by the first argument following
the format argument that has not already received a conversion result. If this object
does not have an appropriate type, or if the result of the conversion cannot be represented
in the object, the behavior is undefined.

Why does printf print the incorrect value?

The following code always prints "0.00". I was expecting "7888". Should I convert it to double?
long l = 7888;
printf("%.2f", l);
%.2f is not a valid format for a long. You can cast it to double:
long l = 7888;
printf("%.2f", (double)l);
Here is a table (scroll a bit down) where you can see which codes are allowed for all number types.
%f expects a double and l variable is a long. printf() does not convert it's arguments to a type required by the format specifier all-by-itself magically.
FWIW, printf() being a variadic function, default argument promotion rule is applied on the supplied arguments, and it does not change a long to double, either. If at all, you want that conversion to happen, you have to cast the argument value explicitly.
You need to write something like
printf("%.2f", (double)l);
Please note, this code invokes undefined behaviour, without an explicit cast. Reference, C11, chapter §7.21.6.1, fprintf()
[....] If any argument is
not the correct type for the corresponding conversion specification, the behavior is undefined.
The %f format specifier expects a double, but you're passing it a long, so that's undefined behavior.
If you want to print it properly, you need to either use the %ld format specifier to print it as a long:
printf("%ld", l);
Or cast l to double to print it as a floating point number:
printf("%.2f", (double)l);
I was expecting "7888".
This happens because you are trying to print LONG with FLOAT identifier.
The compiler complains about that if you turn your setting on:
program.c:5:5: error: format ‘%f’ expects argument of type ‘double’, but argument 2 has type ‘long int’ [-Werror=format=]
printf("%f", l);
^
cc1: all warnings being treated as errors
.
Should I convert it to double?
By the way you can cast it too, if this is what you really need.
I think this is what you realy need:
#include<stdio.h>
int main(void){
long l = 7888;
printf("%ld", l);
return 0;
}
7888
You cannot printf a long proberly with a float identifier. What do you want do achieve?

Resources