6

There is such code:

#include <stdio.h>

int main() {
  float d = 1.0;
  int i = 2;
  printf("%d %d", d, i);
  getchar();
  return 0;
}

And the output is:

0 1072693248

I know that there is error in printf and first %d should be replaced with %f. But why variable i is printed wrong (1072693248 instead of 2)?

scdmb
  • 15,091
  • 21
  • 85
  • 128

5 Answers5

11

Since you specified %d instead of %f, what you're really seeing is the binary representation of d as an integer.

Also, since the datatypes don't match, the code actually has undefined behavior.

EDIT:

Now to explain why you don't see the 2:

float gets promoted to double on the stack. Type double is (in this case) 8 bytes long. However, since your printf specifies two integers (both 4 bytes in this case), you are seeing the binary representations of 1.0 as a type double. The 2 isn't printed because it is beyond the 8 bytes that your printf expects.

Mysticial
  • 464,885
  • 45
  • 335
  • 332
  • 5
    Also note that, because this is a varargs function, the `float` is promoted to a `double`, which is twice the size and therefore takes up the space on the stack for both the `float` and the `int` (on a usual machine). – Chris Lutz Sep 15 '11 at 17:04
  • 2
    This is only true for an ABI which passes all arguments on the stack. @scmdb's program prints "2 0" on my amd64 machine, because the calling code loaded the "1.0" into the first floating-point argument register, and the format string and the "2" into the first and second non-floating point argument registers. The "0" is what happened to be in the third non-FP arg reg. – zwol Sep 15 '11 at 17:37
  • 1
    @Zack: Correct. I just looked up the exact behavior of this: It looks like floats are promoted to double on variable argument functions if they are done on the stack. – Mysticial Sep 15 '11 at 17:41
  • I'm curious, why doesn't the same happen with e.g. long long int (or size_t on a 64-bit computer)? I'm seeing the same weirdness as the questioner, but not when using %d with a long long int argument. Why not? – Quantumboredom Sep 15 '11 at 17:42
  • 1
    The bottom 32-bits of the IEEE double-precision representation of 1.0 is all zeros. So I think it IS being promoted to double. – Mysticial Sep 15 '11 at 17:43
  • It should maybe be clarified that *both of the numbers printed* are part of the binary representation of the `1.0` on the stack (because `double` is twice the width of `int` on this system). I was thinking it was stack garbage instead, because I didn't realize that one of the 32-bit halves of 1.0 (as an IEEE double) is all-bits-zero. – zwol Sep 15 '11 at 17:48
  • Oh, it's definitely being promoted to `double`. That happens independent of where the ABI puts floating-point arguments. It's a consequence of unnamed arguments being treated the same as arguments to unprototyped functions. – zwol Sep 15 '11 at 17:49
  • @Quantumboredom: I'd have to see your code and know exactly which ABI you were compiling for, to know. It's possible that your ABI promotes all integer arguments to 64 bits internally, for instance. – zwol Sep 15 '11 at 17:51
4

printf doesn't just use the format codes to decide how to print its arguments. It uses them to decide how to access its arguments (it uses va_arg internally). Because of this, when you give the wrong format code for the first argument (%d instead of %f) you don't just mess up the printing of the first argument, you make it look in the wrong place for all subsequent arguments. That's why you're getting nonsense for the second argument.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • And due to how varargs works in printf, if you add on "extra" arguments to the printf, it will change what is printed even though they aren't used. – evil otto Sep 15 '11 at 17:10
  • @evil otto: C99 says if you have more arguments than format codes, the extra arguments are ignored. – zwol Sep 15 '11 at 17:38
  • Implementation dependent, I'm sure. Could be my compiler isn't c99 compliant, or it could be undefined behavior which means the compiler can do whatever it wants. – evil otto Sep 15 '11 at 18:05
1

You need to know how printf works. The caller puts all the arguments on the stack. As it parses through the fmt string, the first time it sees a %d it picks the first 4-byte word on the stack and prints it as an integer. The second time it sees a %d, it picks the next 4-byte word. What you're seeing is the raw float bytes being displayed as two integers.

jman
  • 11,334
  • 5
  • 39
  • 61
  • 1
    The caller does not necessarily put all arguments on the stack. That happens to be the case for the 32-bit x86 ABI, but other ABIs commonly put the first few args in registers. – zwol Sep 15 '11 at 17:44
0

Is it signed or unsigned?

Use this as a reference: http://www.lix.polytechnique.fr/~liberti/public/computing/prog/c/C/FUNCTIONS/format.html

apollosoftware.org
  • 12,161
  • 4
  • 48
  • 69
0

A float is stored in memory in a special format, it's not just a number and some decimal places see How to represent FLOAT number in memory in C

Community
  • 1
  • 1
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263