4
printf("%f", 1.0); //prints 1.0

but

printf("%f", 1);  // prints 0.0

How did the conversion happen?

Krisztián Balla
  • 19,223
  • 13
  • 68
  • 84
redkont
  • 337
  • 1
  • 2
  • 12

5 Answers5

3

As per the below @Eric Postpischil's comment different.

The first double argument (or float argument, which will be promoted to double if used with the ... part of a function) is put in %xmm0. The first “normal integer class” type of argument would go into %rdi. For printf, though, that pointer to the format string is the first argument of that class, so it does into %rdi. That means the first int argument passed goes into the second register for that class, which is %rsi. So, with printf("%f", 1);, printf looks for the floating-point value in %xmm0, but the caller puts an int 1 in %rsi

  • i understand you ,but i tought simple , something not be hard like this , – redkont Mar 21 '20 at 11:21
  • 2
    This answer is incorrect to assert that the bytes representing the `int` will be interpreted by `printf` as a floating-point number. In some common C implementations, notably any using the System V x86-64 ABI, the first `double` argument is passed in the `%xmm0` register, while the `int` argument passed to `printf` after the format string is passed in `%rsi`. In processing `%f`, `printf` would see the bytes in `%xmm0` and not those in `%rsi.` – Eric Postpischil Mar 21 '20 at 12:06
  • @EricPostpischil, Thank you so much for letting me know that. You mean to say that, as "1" is an integer constant it will be placed in non %xmm register and when printf scans this %f it will check in %xmm0 and it will get whatever value the register has. Pls, correct me if I am wrong. – Murali Krishna Bellamkonda Mar 21 '20 at 16:15
  • @MuraliKrishnaBellamkonda: Yes. The first `double` argument (or `float` argument, which will be promoted to `double` if used with the `...` part of a function) is put in `%xmm0`. The first “normal integer class” type of argument would go into `%rdi`. For `printf`, though, that pointer to the format string is the first argument of that class, so it does into `%rdi`. That means the first `int` argument passed goes into the second register for that class, which is `%rsi.` So, with `printf("%f", 1);`, `printf` looks for the floating-point value in `%xmm0`, but the caller puts an `int` 1 in `%rsi`. – Eric Postpischil Mar 21 '20 at 16:36
  • Got it now. Thanks again. I have one doubt. How it behaves in a 32-bit machine. – Murali Krishna Bellamkonda Mar 21 '20 at 16:46
  • I think my answer is related to a 32-bit machine Because in 32-bit machine the values are being pushed into the stack of calling function (Actual Copy will be created). Later printf will read the values from the calling function stack. pls, Correct me If I am wrong. – Murali Krishna Bellamkonda Mar 21 '20 at 17:02
3

printf("%f", 1); causes undefined behavior, because double is expected, but you passed an int. There is no explanation of why it prints 0.0, because the behavior is undefined.

Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
  • 1
    There is an explanation of why it prints “0.0”. The C standard does not give that explanation, but an explanation does lie in the source code of the compiler, the ABI of the system, the hardware architecture documentation, and so on. – Eric Postpischil Mar 21 '20 at 12:00
  • @EricPostpischil since no implementation is specified, the question is about the C programming language only. The language provides no reasoning whatsoever, hence the answer. – Aykhan Hagverdili Mar 21 '20 at 12:29
  • 1
    The C standard defines “undefined” to mean “behavior … or which **this document** imposes no requirements” (emphasis added to point out relevant concept). That means the statement “There is no explanation in the C standard” is true. Read literally, the statement “There is no explanation” is false. One might assert the qualifier is to be understood from context. Unfortunately, that is also false: People do not understand this from context. Stack Overflow is rife with the false concept that the C standard is the do-all and end-all of understanding the behavior of C programs. – Eric Postpischil Mar 21 '20 at 12:36
  • 1
    Any competent software engineer **must** gain knowledge of how compilers work, what optimization does to a program, how programs are organized in stacks and data segments and more, how address references work, what sorts of things can go wrong in programs, what observable symptoms are clues to what errors, and so on. These things can be learned only by learning explanations of a great deal more than what is in the C standard. It is pernicious to tell people there is no explanation of behavior not defined by the C standard. – Eric Postpischil Mar 21 '20 at 12:38
  • @EricPostpischil while the things you mentioned are important, trying to explain undefined behavior is inherently wrong. C is a high-level language - it is an abstraction. Telling people not to think in this abstraction is harmful. – Aykhan Hagverdili Mar 21 '20 at 12:47
  • It is not inherently wrong. It is **necessary**. Any competent software engineer **must** gain this knowledge. There is **no benefit** to learning only the abstraction, and there is no basis for the allegation that trying to explain behavior not defined by the C standard is inherently wrong—there is no logical principle leading to that conclusion, no real-world practical value to it, nothing. It is bunk. – Eric Postpischil Mar 21 '20 at 12:57
2

Not every compiler behaves like this, some actually print 1.0. But when instruct printf to print a double value, you must pass it a double value, not an integer. You can always use a type cast:

printf("%f", (double)1);
Marc Balmer
  • 1,780
  • 1
  • 11
  • 18
2

The question is not about printf function itself, the question is if the compiler is smart enough. If your compiler is not smart enough, then it treats printf as just a normal function call and does not know anything about the syntax of arguments for this function. So it just puts a string and an integer number on the stack and calls the function. The printf function takes the first argument and starts to parse it as a format string. When it sees format specifier %f it attempts to interpret the corresponding part of the memory at the stack as a floating point number. It has no way to know that compilator pushed int value there before. So printf does it best to interpret the memory as a floating point number. The result is platform dependent, i.e. on endiness and float/int sizes, and also includes randomness, because you'll most probably hit some garbage on the stack. The transformation done by printf in this case can be seen also like this:

int i = 1;               // Integer variable
int* pi = &i;            // Pointer to i 
float* pf = (float*)pi;  // Reinterpret the pointer as floating point number address
float f = *pf;           // Get the floating point from this address
printf("%f\n", f);
Alex Sveshnikov
  • 4,214
  • 1
  • 10
  • 26
  • 1
    In some common C implementations, such as anything using the System V x86-64 ABI, neither the string nor the `int` are put onto the stack. A pointer to the first character of the string is put into a register, and the `int` is put into another register. If a `double` had been passed instead of the `int` 1, it would be put into a completely different register. The result of this is that, when `printf` looks for the `double` for `%f`, it does not see the bytes of the int `1`. It sees the bytes in a different register. – Eric Postpischil Mar 21 '20 at 12:02
  • I agree it's nice to understand what's happening under the hood, but since it is all indeed platform specific, you should say which platforms your explanation covers. – Nate Eldredge Mar 21 '20 at 18:05
1

The thing here printf() will except to receive float based on the format you passed in, to print int as float in printf() you have to cast it

printf("%f", (float)1);

or

printf("%f",(double)1);

because C will treat the variables passed to printf() based on their types and memory representation and you pass the wrong value it will result in undefined behavior.

ROOT
  • 11,363
  • 5
  • 30
  • 45