Is printf("%d", 1.0) undefined? - c

According to section 4.9.6.1 of the C89 draft, %d is a character that specifies the type of conversion to be applied.
The word conversion implies, in my opinion, that printf("%d", 1.0) is defined.
Please confirm or refute this.

The conversion is the conversion of a language value to a lexical representation of that value.
Your theory is wrong; behavior is undefined. The spec says (7.19.6.1p8 and 9, using C99 TC2):
The int argument is converted to signed decimal in the style [−]dddd.
And
If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Printf is a varargs function, so no conversion is possible. The compiler just arranges to push a double onto the arguments list. Printf has no way to find out that it's a double versus an int versus an elephant. Result? Chaos.

The word "conversion" here is referring to the conversion of an int (which is the only acceptable argument type here) to a string of characters that make of the decimal representation of that int. It has nothing to do with conversion from other types (such as double) to int.

Not sure if it's officially undefined or an error - but it's wrong!

Related

Do format specifiers perform implicit type conversion?

#include <stdio.h>
int main(void) {
int x = 5;
int y = &x;
printf("Signed Value of Y: %d \n", y);
printf("Unsigned Value of Y: %u", y);
return 0;
}
Since y is of int type, using %d gives a possibly-signed output, whereas %u gives an unsigned output. But y is of int type, so why does %u give unsigned output? Is there an implicit type conversion?
"Re: But y is of int type, So why does %u give unsigned output?"
From C11:
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.
where,
undefined — The behavior for something incorrect, on which the
standard does not impose any requirements. Anything is allowed to
happen, from nothing, to a warning message to program termination, to
CPU meltdown, to launching nuclear missiles (assuming you have the
correct hardware option installed).
— Expert C Programming.
Effectively, a printf call is two separate things:
All the arguments are prepared to send to the function.
The function interprets the format string and its other arguments.
In any function call, the arguments are prepared according to rules involving the argument types and the function declaration. They do not depend on the values of the arguments, including the contents of any string passed as an argument, and this is true of printf too.
In a function call, the rules are largely (omitting some details):
If the argument corresponds to a declared parameter type, it is converted to that type.
Otherwise (if the argument corresponds to the ... part of a function declaration or the called function is declared without specifying parameter types), some default promotions are applied. For integers, these are the integer promotions, which largely (omitting some details) convert types narrower than int to int. For floating-point, float is promoted to double.
printf is declared as int printf(const char * restrict format, ...);, so all its arguments other than the format string correspond to ....
Inside printf, the function examines its format string and attempts to perform the directives given in the format string. For example, if a directive is %g, printf expects a double argument and takes bits from the place it expects a double argument to be passed. Then it interprets those bits as a double, constructs a string according to the directive, and writes the string to standard output.
For a %d or %u directive, printf expects an int or an unsigned int argument, respectively. In either case, it takes bits from the place it expects an int or an unsigned int argument to be passed. In all C implementations I am aware of, an int and an unsigned int argument are passed in the same place. So, if you pass an int argument but use %u, printf will get the bits of an int but will treat them as if they were the bits of an unsigned int. No actual conversion has been performed; printf is merely interpreting the bits differently.
The C standard does not define the behavior when you do this, and a C implementation would be conforming to the standard if it crashed when you did this or if it processed the bits differently. You should avoid it.
Is there an implicit type conversion?
Sort of. A function such as printf that accepts a variable number of arguments does not automatically know the number of variable arguments it actually receives on any call, or their types. Conversion specifications such as %d and %u collectively tell printf() how many variable arguments to expect, and individually they tell printf what type to expect for each argument. printf() will try to interpret the argument list according to these specifications.
The C language specification explicitly declines to say what happens when the types of printf arguments do not correspond properly to the conversion specifications in the accompanying format string. In practice, however, some pairs of data types have representations similar enough to each other that printf()'s attempt to interpret data of one type as if it were the other type is likely (but not guaranteed) to give the appearance of an implicit conversion from one type to the other. Corresponding signed and unsigned integer types are typically such pairs.
You should not rely on such apparent conversions actually happening. Instead, properly match argument types with conversion specifications. Correct mismatches by choosing a different conversion specification or performing an appropriate explicit type conversion (a typecast) on the argument.

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

When can I get away with not declaring int with signed?

In C, signed integers like -1 are supposedly supposed to be declared with the keyword signed, like so:
signed int i = -1;
However, I tried this:
signed int i = -2;
unsigned int i = -2;
int i = -2;
and all 3 cases print out -2 with printf("%d", i);. Why?
Since you confirmed you are printing using:
printf("%d", i);
this is undefined behavior in the unsigned case. This is covered in the draft C99 standard section 7.19.6.1 The fprintf function which also covers printf for format specifiers, it says in paragraph 9:
If a conversion specification is invalid, the behavior is undefined.248)[...]
The standard defined in section 3.4.3 undefined behavior as:
behavior, upon use of a nonportable or erroneous program construct or of erroneous data,
for which this International Standard imposes no requirements
and further notes:
Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).
Finally, we can see that int is the same as signed int. We can see this by going to section 6.7.2 Type specifiers, in paragraph 2 it groups int as follows:
int, signed, or signed int
and later on says in paragraph 5 says:
Each of the comma-separated sets designates the same type, except that for bit-field[...]
The way an integer variable is printed, is subjected to the format string that you pass to printf:
If you use %d, then you'll be printing it as a signed integer.
If you use %u, then you'll be printing it as an unsigned integer.
printf has no way of knowing what you pass to it. C compiler does the default type promotions on passing the arguments, and then the function itself reinterprets the values in accordance with the format specifiers that you pass, because it has no other information regarding the type of the value that you passed.
When you pass an unsigned int to printf in a position of %d, it is undefined behavior. Your program is incorrect, and it could print anything.
It happens that on hardware that represent negative numbers in two's complement representation you get the same number that you started with. However, this is not a universal rule.
unsigned int i = -2; // i actually holds 4294967294
printf("%d", i); // printf casts i back to an int which is -2 hence the same output
You've got 2 things going on:
Signed and unsigned are different ways of interpreting the same 64 (or 32, or whatever) bits.
Printf is a variadic function which accepts parameters of different types
You passed a signed value (-2) to an unsigned variable, and then asked printf to interpret it as signed.
Remember that "signed" and unsigned have to do with how arithmetic is done on the numbers.
the printf family accepts internally casts whatever you pass in based on the format designators. (this is the nature of variadic functions that accept more than one type of parameter. They cannot use traditional type safety mechanisms)
This is all very well, but not all things will work the same.
Addition and subtraction work the same on most architectures (as long as you're not on some oddball architecture that doesn't use 2's complement for representing negative numbers
Multiplication and division may also work the same.
Inequality comparisons are the hardest thing to know how they will work, and I have been bit a number of times doing a comparison between signed and unsigned that I thought would be ok, be cause they were in the small signed number range.
Thats what "undefined" means. Behaviour is left to the compiler and hardware implementers and cannot be relied to be the same between architectures or even over time on the same architecture.

Puzzled about printf output

While using printf with %d as format specifier and giving a float as an argument e.g, 2.345, it prints 1546188227. So, I understand that it may be due to the conversion of single point float precision format to simple decimal format. But when we print 2.000 with the %d as format specifier, then why it prints 0 only ?
Please help.
Format specifier %d can only be used with values of type int (and compatible types). Trying to use %d with float or any other types produces undefined behavior. That's the only explanation that truly applies here. From the language point of view the output you see is essentially random. And you are not guaranteed to get any output at all.
If you are still interested in investigating the specific reason for the output you see (however little sense it makes), you'll have to perform a platform-specific investigation, because the actual behavior depends critically on various implementation details. And you are not even mentioning your platform in your post.
In any case, as a side note, note that it is impossible to pass float values as variadic arguments to variadic functions. float values in such cases are always converted to double and passed as double. So in your case it is double values you are attempting to print. Behavior is still undefined though.
Go here, enter 2.345, click "rounded". Observe 64-bit hex value: 4002C28F5C28F5C3. Observe that 1546188227 is 0x5c28f5c3.
Now repeat for 2.00. Observe that 64-bit hex value is 4000000000000000
P.S. When you say that you give a float argument, what you apparently mean is that you give a double argument.
Here what ISO/IEC 9899:1999 standard $7.19.6 states:
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.
If you're trying to make it print integer values, cast the floats to ints in the call:
printf("ints: %d %d", (int) 2.345, (int) 2.000);
Otherwise, use the floating point format identifier:
printf("floats: %f %f", 2.345, 2.000);
When you use printf with wrong format specifier for the corresponding argument, the result is undefined behavior. It could be anything and may differ from one implementation to another. Only correct use of format specified has defined behavior.
First a small nitpick: The literal 2.345 is actually of the type double, not float, and besides, even a float, such as the literal 2.345f, would be converted to double when used as an argument to a function that takes a variable number of arguments, such as printf.
But what happens here is that the (typically) 64 bits of the double value is sent to printf, and then it interprets (typically) 32 of those bits as an integer value. So it just happens that those bits were zero.
According to the standard, this is what is called undefined behavior: The compiler is allowed to do anything at all.

Resources