c-behaviour of printf("%d", <double>) [duplicate] - c

This question already has answers here:
Given the state of the stack and registers, can we predict the outcome of printf's undefined behavior
(2 answers)
Closed 6 years ago.
Why does this code prints nonsense values? If it makes sense then what is it?
printf("%d\n", 5.0 / 4);
By the way, I know about format specifiers I should be using %f instead of %d. but I want to know what c actually does.
Strangely, every time I run the compiled program, it prints a different thing. doesn't it have to be deterministic?
As far as I could observe, this code prints a similar thing:
float c;
printf("%d\n", &c);
are they any related?
and when i tried:
float c;
printf("%d\n%d\n", c, &c);
There is a constant 252 between those two values. 256 - sizeof(float) maybe?
and declaring c as a double makes the difference 0.
Thanks in advance!
UPDATE: writing the same code on different machines yielded different results.(252 being 56. former is a 64-bit ubuntu machine and latter is 64-bit OS X)

printf is unusual in that it has no single function signature. In other words, there's no mechanism to automatically take the actual arguments you pass and convert them into the type that's expected. Whatever you try to pass, gets passed. If the type you pass does not match the type expected by the corresponding format specifier, you get nonsense.
Furthermore, if the mismatched types you're passing versus what the format specifiers are expecting have different sizes, printf can end up popping random garbage off the stack, that has nothing to do with what you thought you passed.
You asked, "doesn't it have to be deterministic?", and the answer there is a very emphatic "NO"! By passing the wrong type of value to printf, you have invoked undefined behavior, which means that anything can happen, and it very definitely does not have to be the same thing twice.
(Even though they're not required to, some compilers peek at the format string and try to check the values you passed against the values that are expected. For example, under gcc, your first code fragment gets the warning "format ‘%d’ expects type ‘int’, but argument 2 has type ‘double’". But I don't know of a compiler that will try to do conversions to "fix" the arguments.)

Related

Why does an integer interpreted as a double render zero?

printf("%f", 20); results in the output 0.000000, not 20.000000. I'm guessing this has to do with how an int is represented in memory and how a double is represented in memory. Surprisingly for me, no matter how I alter 20, for example by making the number larger, the output is still 0.000000. Could someone please explain the underlying mechanics of this?
Most probably you are compiling your code on a platform/ABI where even for varargs functions data is passed into registers, and in particular different registers for integer/floating point values. x86_64 on Linux/OS X behaves like that.
The caller has an integer to pass, so it puts it into rsi; on the other side, printf expects a floating point value, so it tries to read it from xmm0. No matter how you change your integer argument to any other value printf will be unaffected - if will just print whatever happens to stay into xmm0 at the moment of the call.
You can actually check if this is the case by changing your call to:
printf("%f", 20, 123.45);
if it's working as I described, you should see 123.45 printed (the caller here puts 123.45 into xmm0, as it is the first floating point parameter passed to the function; printf behaves as before, but this time finds another value into xmm0).
First of all, this is undefined behavior. %f expects an argument of type float/double. Passing an int makes incompatible type and hence it invokes UB.
Quoting C11, chapter §7.21.6.1, fprintf()
f,F
A double argument representing a floating-point number [...]
a float is also allowed, as due to default argument promotion rule, it will get promoted to a double which is the expected type there, so either a double or a float is acceptable, but not an int.
...and that's all. UB, is, well, UB. You cannot try to justify anything with a code producing UB.
That said, with proper warning levels enabled, the code should not compile, at all. Though, if you choose to make the code compile and produce a binary/assembly code you can see different code generated for different platforms. One of such cases is explained in the other answer by Matteo Italia, considering x86_64 arch on Linux/OS X.
The problem is your compiler is assuming the 20 is an int. Your options are to either declare a float variable and input it here OR add a typecast.
e.g.
printf("%f", (double)20);

C 's format specifier

I have this code in C-
#include <stdio.h>
void main(void)
{
int a=90;
float b=4;
printf("%f",90%4);
}
It gives an output 0.0000,I am unable to understand why???
i know that 90%4 returns 2 and the format specifier specified is %f,which is for double,but what I expect is-
That it will give an error,but it is showing 0.0000 as output.
Can someone please explain why?
The type of 90%4 will be int.
The behaviour on using %f as the format specifier for an int is undefined.
The output could be 2, it could be 0. The compiler could even eat your cat.
This discrepancy comes about because the compiler and library do not communicate regarding types. What happens is that your C compiler observes that printf is a variadic function taking any number of arguments, so the extra arguments get passed per their individual types. If you're lucky, it also parses the format string and warns you that the type doesn't match:
$ gcc -Wformat -o fmterr fmterr.c
fmterr.c: In function ‘main’:
fmterr.c:6:2: warning: format ‘%f’ expects argument of type ‘double’,
but argument 2 has type ‘int’ [-Wformat=]
printf("%f",90%4);
^
But this is still just a warning; you might have replaced printf with a function with different behaviour, as far as the compiler is concerned. At run time, floating point and integer arguments may not even be placed in the same place, and certainly don't have the same format, so the particular result of 0.0 is not guaranteed. What really happens may be related to the platform ABI. You can get specified behaviour by changing the printf argument to something like (float)(90%4).
printf is a variadic function. Such functions are obscure, with no type safety worth mentioning. What such functions do is to implicitly promote small integer types to type int and float to double, but that is it. It is not able to do more subtle things like integer to float conversion.
So printf in itself can't tell what you passed on it, it relies on the programmer to specify the correct type. Integer literals such as 90 are of type int. With the %f specifier you told printf that you passed a double, but actually passed an int, so you invoke undefined behavior. Meaning that anything can happen: incorrect prints, correct prints, program crash etc.
An explicit cast (double)(90%4) will solve the problem.
%f expects a double and you pass int(90%4=2) in printf. Thus ,leading to Undefined Behaiour and can give output anything .
You need to explicitly cast -
printf("%f",(double)(90%4));
Don't try this as compiler will generate an error (as pointed by #chux Sir )-
printf("%f",90%(double)4);
In short: there is no error checking for format specifiers. If your format is looking for a double, then whatever you pass as an argument (or even if you pass nothing) will be interpreted as a double.
By default, 90%4 gives an integer.
If you print integer with %f specifier, it will print 0.
For example, printf("%f", 2); will print 0.
You typecast result with float, you will get 2.00000.
printf("%f",(float) (90%4)); will print 2.00000
Hope it clarifies.

Why does %d show two different values for *b and *c in the code [b and c points to same address]

Consider the Code below
{
float a=7.999,*b,*c;
b=&a;c=b;
printf("%d-b\n%d-c\n%d-a\n",*b,*c,a);
}
OUTPUT:
-536870912-b
1075838713-c
-536870912-a
I know we are not allowed to use %d instead of %f, but why does *b and *c give two different values?
Both have the same address, can someone explain?
I want to know the logic behind it
Here is a simplified example of your ordeal:
#include <stdio.h>
int main() {
float a=7.999, b=7.999;
printf("%d-a\n%d-b\n",a,b);
}
What's happening is that a and b are converted to doubles (8 bytes each) for the call to printf (since it is variadic). Inside the printf function, the given data, 16 bytes, is printed as two 4-byte ints. So you're printing the first and second half of one of the given double's data as ints.
Try changing the printf call to this and you'll see both halves of both doubles as ints:
printf("%d-a1\n%d-a2\n%d-b1\n%d-b2\n",a,b);
I should add that as far as the standard is concerned, this is simply "undefined behavior", so the above interpretation (tested on gcc) would apply only to certain implementations.
There can be any number of reasons.
The most obvious -- your platform passes some integers in integer registers and some floating point numbers in floating point registers, causing printf to look in registers that have never been set to any particular value.
Another possibility -- the variables are different sizes. So printf is looking in data that wasn't set or was set as part of some other operation.
Because printf takes its parameters through ..., type agreement is essential to ensure the implementation of the function is even looking in the right places for its parameters.
You would have to have deep knowledge of your platform and/or dig into the generated assembly code to know for sure.
Using wrong conversion specification invokes undefined behavior. You may get either expected or unexpected value. Your program may crash, may give different result on different compiler or any unexpected behavior.
C11: 7.21.6 Formatted input/output functions:
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.
// Bad
float a=7.999,*b,*c;
b=&a;c=b;
printf("%d-b\n%d-c\n%d-a\n",*b,*c,a);
// Good
float a=7.999,*b,*c;
b=&a;c=b;
printf("%f-b\n%f-c\n%f-a\n",*b,*c,a);
Using an integer format specified "%d" instead of correctly using a float specified "%f" is what alk elliptically fails to explain as "undefined behavior".
You need to use the correct format specifier.

printf function in c [duplicate]

This question already has answers here:
float to int unexpected behaviour
(6 answers)
Closed 6 years ago.
here are part of my code.
float a = 12.5;
printf("%d\n", a);
printf("%d\n", (int)a);
printf("%d\n", *(int *)&a);
when I compile in windows, I got:
0
12
1094713344
and then, I compile in linux, I got:
-1437851864
12
1094713344
-1437851864 will be changed every time I excuted it.
my question is: in how does the "printf" function works in linux
It works very well, but why are you passing the wrong sort of data to it? The %d specifier expects and int, but you're passing something else. Bad idea.
If float and int are differently sized across the varargs barrier, this is undefined behavior. And since float is typically promoted to double with varargs calls, if your int is smaller than your double this will break.
In short, this is really bad and broken code. Don't do this.
To print a floating point number in C, you should do:
float a = 12.5;
printf("%f\n", a);
As has been mentioned, passing arguments with types not matching the format string invokes undefined behaviour, so the language standard doesn't place any restrictions on what
float a = 12.5;
printf("%d\n", a);
actually does.
To find out what it does, you'd need to analyse your implementation, or at least the assembly the compiler produced for that code.
A common way for translating that code is to pass the promoted (to double) float argument in a floating point register and tell printf how many arguments are passed in floating point registers. But since the format tells printf to look for an int, it doesn't look in a floating point register for it, but in another register. So the printed value would be whatever happened to be in that register when printf was called.

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