In C you have the "%c" and "%f" formats flags for printf- and scanf-like functions. Both of these function use variable length arguments ..., which always convert floats to doubles and chars to ints.
My question is, if this conversion occurs, why do separate flags for char and float exist? Why not just use the same flags as for int and double?
Related question:
Why does scanf() need "%lf" for doubles, when printf() is okay with just "%f"?
Because the way it gets printed out is different.
printf("%d \n",100); //prints 100
printf("%c \n",100); //prints d - the ascii character represented by 100
Because float and double have different machine representations or sizes, and calling conventions: many processors have registers dedicated to floating point which might be used for argument passing.
And the C standard requires that short arguments are converted to int and float arguments are converted to double.
Related
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();
}
I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).
I know that in other languages such as Java when I use System.out.println(); and I put a variable inside of it like an int that holds the number 22 it will print 22 to the console.
In C if I do the same thing with printf(); I need to specify the type in the string such as printf("%d", n); I also know that Java has its own printf function.
What I am trying to get at here is how the C control String works compared to other languages such as Java where you don't have to provide the type identifier in the System.out.println(); and it automatically recognizes the variable is an int.
Is this part of C's way of efficiency and does it not actually check the type and rely's on the programmer to understand the type they are providing?
In fact, printf is neither efficient nor type-safe way of writing data.
There is a performance penalty because format string is parsed at compile time and type specific actions are chosen at run time as well, whereas in C++ and Java it can be done at compile time. Moreover, variadic arguments are forced to be passed on the stack, that is less efficient than passing them in registers.
And what is even more important is that printf is not type-safe. It is possible to pass any number of parameters of any types to printf, ignoring format string prescriptions. Of course, it can easily trigger undefined behavior.
The only reason for such behavior is that there is no function overloading in C.
On the other hand, it's not that bad. First, most of the compilers parse format string and issue a warning if it isn't consistent with parameters passed to printf. Second, aforementioned performance penalty is in fact negligible compared to the cost of formatting and printing text after types have been deduced.
Both C's predefined data types (integers, characters, etc.) as well as user defined types carry no type information with them at run-time. Thus if you use a 4 byte integer in your code, it occupies only 4 bytes in memory (disregarding any padding needed for alignment). This is great for efficiency reasons, but it means that functions that handle multiple data types (like printf) need to be told via the format/control string what the types of the arguments are.
So when printf receives a format string of "%d %f" it knows that the type of the first non-format argument is integer and the second argument is of type float.
By passing control characters in printf() we tell the compiler how to allocate the memory and what is the range of the data that we wanna to print.
Control strings are also known as format specifiers. They are used in formatted input and output operations of the data. Remember, format specifiers are used for formatting the data, for example, in printf() and scanf().
Control strings in scanf() are used to transfer the data to the processor's memory in a formatted way, whereas printf () transfers the data to the output device e.g. monitor screen in a formatted way.
Some common Format Specifierss are listed below:
________________________________________________________________
FORMAT SPECIFIER :: DESCRIPTION
________________________________________________________________
%c Character
%d Signed Integer
%e or %E Scientific notation
%f Floating point
%g or %G. Similar as %e or %E
%hi. Signed Integer
%hu Unsigned Integer(Short)
%i Signed Integer
%l or %ld or %li Signed Integer
%lf Floating point
%Lf Floating point
%lu Unsigned integer
%lli, %lld Signed long long int
%llu. unsigned long long int
%o. Octal representation of Integer.
%p Address of pointer to void void *
void *
%s String char *
%u Unsigned int
Unsigned short int
%n Prints nothing
%% Prints % character
%o Octal representation
%p Address of pointer to void void *
void *
%s String char *
%u Unsigned Integer
%x or %X Hexadecimal representation of
Unsigned Int.
%n Prints nothing
%% Prints % character
________________________________________________________________
I tried to print character as a float in printf and got output 0. What is the reason for this.
Also:
char c='z';
printf("%f %X",c,c);
is giving some weird output for hexadecimal while output is correct when I do this:
printf("%X",c);
why is it so?
The printf() function is a variadic function, which means that you can pass a variable number of arguments of unspecified types to it. This also means that the compiler doesn't know what type of arguments the function expects, and so it cannot convert the arguments to the correct types. (Modern compilers can warn you if you get the arguments wrong to printf, if you invoke it with enough warning flags.)
For historical reasons, you can not pass an integer argument of smaller rank than int, or a floating type of smaller rank than double to a variadic function. A float will be converted to double and a char will be converted to int (or unsigned int on bizarre implementations) through a process called the default argument promotions.
When printf parses its parameters (arguments are passed to a function, parameters are what the function receives), it retrieves them using whatever method is appropriate for the type specified by the format string. The "%f" specifier expects a double. The "%X" specifier expects an unsigned int.
If you pass an int and printf tries to retrieve a double, you invoke undefined behaviour.
If you pass an int and printf tries to retrieve an unsigned int, you invoke undefined behaviour.
Undefined behaviour may include (but is not limited to) printing strange values, crashing your program or (the most insidious of them all) doing exactly what you expect.
Source: n1570 (The final public draft of the current C standard)
You need to use a cast operator like this:
char c = 'z';
printf("%f %X", (float)c, c);
or
printf("%f %X", (double)c, c);
In Xcode, if I do not do this, I get the warning:
Format specifies specifies 'double' but the argument has type 'char', and the output is 0.000000.
I tried to print character as a float in printf and got output 0. What is the reason for this.
The question is, what value did you expect to see? Why would you expect something other than 0?
The short answer to your question is that the behavior of printf is undefined if the type of the argument doesn't match the conversion specifier. The %f conversion specifier expects its corresponding argument to have type double; if it isn't, all bets are off, and the exact output will vary.
To understand the floating point issue, consider reading: http://en.wikipedia.org/wiki/IEEE_floating_point
As for hexadecimal, let me guess.. the output was something like... 99?
This is because of encodings.. the machine has to represent information in some format, and usually that format entails either giving meanings to certain bits in a number, or having a table of symbols to numbers, or both
Floating points are sometimes represented as a (sign,mantissa,exponent) triplet all packed in a 32 or 64 bit number - characters are sometimes represented in a format named ASCII, which establishes which number corresponds to each character you type
Because printf, like any function that work with varargs, eg: int foobar(const char fmt, ...) {} tries to interpret its parameter to certain type.
If you say "%f", then pass c (as a char), then printf will try to read a float.
You can read more here: var_arg (even if this is C++, it still applies).
int main()
{
int x;
float y;
char c;
x = -4443;
y = 24.25;
c = 'M';
printf("\nThe value of integer variable x is %f", (float)x);
printf("\nThe value of float variable y is %d", y);
printf("\nThe value of character variable c is %f\n",c);
return 0;
}
Output:
The value of integer variable x is -4443.000000
The value of float variable y is 0
The value of character variable c is 24.250000
Why am I not getting the expected output?
But when I am using external casting I am getting expected output which is:
The value of integer variable x is -4443.000000
The value of float variable y is 24
The value of character variable c is 77.000000
why i am not getting the expected output ?
Short answer: Because your expectations are wrong.
You're instructing the compiler to read an integer from where y is. Which is wrong. Format specifier don't tell the compiler to do casts, just what type to expect, and trust you to provide the right type.
The behaviour can be due to the fact that, for example, a float is stored in 8 bytes. The high-order bytes will be 0 in this case. But an int is stored in 4 bytes. So you tell the compiler read the int from where y is, it reads the first 4 bytes, which are 0, and prints 0...
EDIT: As John pointed out in the comments, this is UB, which means that anything can happen:
7.21.6.1/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.
Many computing platforms pass different types of arguments in different ways. On some platforms, floating-point arguments are passed in special floating-point registers. On most platforms, integer arguments are passed in general processor registers. Large arguments, such as structures, are stored somewhere in memory, and a pointer is passed instead (invisibly to the C source code). Once the few registers available for arguments are used, the remaining arguments are typically passed on the stack.
When you call printf, the compiler does not match the arguments you pass to the conversion specifiers in the format string. (Except that a good compiler will check and issue a warning if the types do not match.) In order to operate, the printf routine reads the format string and, when it finds a conversion specifier, it reads data from where the corresponding argument should be. If you specify “%d” but pass a float, the printf routine may read data from a general processor register, but the float value is in a floating-point register. Therefore, the value that is printed will be whatever data happened to be in the general processor register.
Similarly, when you specify “%f” but pass an integer, the printf routine may read from a floating-point register, but the integer value is in a general processor register.
The compiler will not convert printf arguments to the target type and might not warn you about the mismatches. You must match the conversion specifiers in the format string to the argument types.
Bonus: Here are documents describing how arguments are passed to subroutines on one platform (Mac OS X).
You cannot format a char as a float "%f", use "%c" or "%d" instead. I find that http://www.cplusplus.com/reference/clibrary/cstdio/printf/ is a good reference.
The format specifiers and the types of the arguments don't match, which I believe causes undefined behavior. printf doesn't do casting for you, so you have to explicitly cast the arguments.