Is passing a float to printf undefined behavior? - c

The C99 standard talks about doubles in the specification for fprintf (which subsequently applies to printf). It says "a double argument representing a floating-point number is converted..." Then in paragraph 9, it says:
If a conversion specification is invalid, the behavior is undefined.
If any argument is not the correct type for the corresponding
specification, the behavior is undefined.
So I would expect the following to be undefined behavior but my compiler doesn't warn about it.
double d = 2.0;
float f = 2.0f;
printf("%f", d);
printf("%f", f); // here
On the other hand, the specification for fscanf says "floating point number" instead of double. Is it undefined behavior like this user claims?

Passing a float to printf is not undefined behavior--it's simply an impossibility. The float will be promoted to double before printf receives it.
scanf is different because what you're (at least normally) passing to scanf are pointers rather than the data objects themselves. Since you're passing a pointer to the original data, with scanf you need to distinguish between a float and a double.

float in vararg functions are always promoted to double.
scanf deals with pointers.

Related

why printf behaves differently when we try to print float as a hexadecimal? [duplicate]

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).

why printf behaves differently when we try to print character as a float and as a hexadecimal?

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).

printf value in union depending on format

It is possible to scanf values into a union depending on the format specifier.
union {
int i;
double f;
} u;
scanf("%lf", &u.i); // implicitly scan the double
printf("%lf", u.f); // explicitly print the double
This works because scanf takes pointers as arguments, and all elements in the union are aligned, (void*)&u.i == (void*)&u.f.
Is it possible to change the printf line in a way that does the same thing, picking the correct value to print from the union based on the format, without accidentally slicing some of the possible values?
printf("%lf", ???); // implicitly print the double
printf cannot infer the type of the arguments passed to it. The argument will be interpreted as per the corresponding conversion specifier in the format string. Therefore, the arguments must correspond properly to their respective conversion specifiers. Quoting C11 standard § 7.21.6.1 ¶9 about fprintf
If a conversion specification is invalid, the behavior is undefined.
If any argument is not the correct type for the corresponding
conversion specification, the behavior is undefined.
Again quoting from the C11 standard § 7.21.6.2 ¶13 about fscanf
If a conversion specification is invalid, the behavior is undefined.
Therefore, the following call to scanf invokes undefined behaviour because u.i is of type int but %lf conversion specifier means you are reading a double.
scanf("%lf", &u.i);
Please read these -
What happens when I use the wrong format specifier?
Wrong format specifiers in scanf or printf
Also, it's undefined behaviour to access the field of a union which was not most recently written.
union {
int i;
double f;
} u;
// undefined behaviour. u.i is of type int but %lf
// means scanf will read a double
scanf("%lf", &u.i);
// undefined behaviour because reading the field u.f
// but the last written field is u.i
printf("%lf", u.f);
No, you can't do that.
There's no indirect printf() format specifier, and different members of the union can have different size and thus be handled differently as a varargs parameter.
Even if there was a way to say "pointer to integer" instead of just "integer", there would still of course be no way to have printf() decide between "pointer to integer" and "pointer to double" based only on that pointer value.
If I understood you correctly, you'd like something like:
printf("%>g\n", &u); /* prints the double */
printf("%>d\n", &u); /* prints the int */
Where I invented (!) the > format modifier to say "the argument is a pointer to the corresponding data type, i.e. >g is "pointer todouble`". Sadly this is not standard. You can of course implement this yourself.

Can anyone explain the output

If I try to print a float as an int, this code:
main () {
float a = 6.8f;
printf("%d", a);
}
prints 1073741824, while this code:
main () {
float a = 9.5f;
printf("%d", a);
}
prints 0.
Is the output undefined? Also when is %f used with integer and %d used with double?
Not only the output, but the entire program has undefined behavior, since type of the value you are passing to printf() does not match the type the format string expects.
From the C99 standard section 7.19.6.1 The fprintf function:
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.
%d expects an int, not a float, so the program has undefined behaviour (including the output).
As described in previous answers if the print format doesn't match the type passed, it shows undefined behavior.
If you want to view integer as float u need to typecast it.
int j = 5;
printf("%f",(float)(j));
This will print output as 5.0 ie as a floating digit number
The C Standard says that the printf format must match the type passed in. If it doesn't, the behavior is expressly undefined:
C99, 7.19.6.1 # 9 (fprintf)
If a conversion specification is invalid, the behavior is
undefined.239) If any argument is not the correct type for the
corresponding conversion specification, the behavior is undefined.

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