Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integer value of floating type in C

#include<stdio.h>
 int main()
 {
   float a;
   printf("Enter a number:");
   scanf("%f",&a);
   printf("%d",a);
   return 0;
 }

I am running the program with gcc in Ubuntu. For values--

          3.3 it gives value 1610612736 
          3.4 it gives value 1073741824
          3.5 it gives value 0
          3.6 it gives value -1073741824
          4 it gives value 0
          5 it gives value 0

What is happening? Why are these values printed? I'm doing this intentionally, but would like to understand why this is happening. Details are appreciated!

like image 541
pierokr Avatar asked Dec 29 '11 18:12

pierokr


2 Answers

The printf function does not know the type of format you passed in, because that part is variadic.

int printf(const char* format, ...);
//                             ^^^

In the C standard, passing a float will be automatically promoted to a double (C11§6.5.2.2/6), and nothing else will be done in the caller side.

Inside printf, since it doesn't know the type of that ... thingie (§6.7.6.3/9), it has to use the hint from elsewhere — the format string. Since you've passed "%d", it is telling the function that, an int is expected.

According to the C standard, this leads to undefined behavior (§7.21.6.1/8–9), which includes the possibility of printing some weird number, end of story.

But what is really happening? In most platforms, a double is represented as "IEEE 754 binary64" format, and a float in binary32 format. The numbers you've entered are converted to a float, which only has 23 bits of significance, which means the numbers will be approximated like this:

3.3 ~ (0b1.10100110011001100110011) × 2¹  (actually: 3.2999999523162842...)
3.4 ~ (0b1.10110011001100110011010) × 2¹  (actually: 3.4000000953674316...)
3.5 = (0b1.11                     ) × 2¹  (actually: 3.5)
3.6 ~ (0b1.11001100110011001100110) × 2¹  (actually: 3.5999999046325684...)
4   = (0b1                        ) × 2²  (actually: 4)
5   = (0b1.01                     ) × 2²  (actually: 5)

Now we convert this to double, which has 53 bits of significance, which we have to insert 30 binary "0"'s at the end of these numbers, to produce e.g.

3.299999952316284 = 0b1.10100110011001100110011000000000000000000000000000000 ×2¹

These are mainly to derive the actual representation of those numbers, which are:

3.3 → 400A6666 60000000
3.4 → 400B3333 40000000
3.5 → 400C0000 00000000
3.6 → 400CCCCC C0000000
4   → 40100000 00000000
5   → 40140000 00000000

I recommend using http://www.binaryconvert.com/convert_double.html to see how this breaks down to the ±m × 2e format.

Anyway, I suppose your system is an x86/x86_64/ARM in normal setting, which means the numbers are laid out in memory using little-endian format, so the arguments passed will be like

 byte
  #0   #1   ...          #4   ...            #8 ....
+----+----+----+----+  +----+----+----+----+----+----+----+----+
| 08 | 10 | 02 | 00 |  | 00 | 00 | 00 | 60 | 66 | 66 | 0A | 40 | ....
+----+----+----+----+  +----+----+----+----+----+----+----+----+
 address of "%d"         content of 3.299999952316284
 (just an example)

Inside the printf, it consumes the format string "%d", parses it, and then finds out that an int is needed because of %d, so 4 bytes are taken from the variadic input, which is:

 byte
  #0   #1   ...          #4   ...            #8 ....
+ - -+ - -+ - -+ - -+  +====+====+====+====+ - -+ - -+ - -+ - -+
: 08 : 10 : 02 : 00 :  | 00 | 00 | 00 | 60 | 66 : 66 : 0A : 40 : ....
+ - -+ - -+ - -+ - -+  +====+====+====+====+ - -+ - -+ - -+ - -+
 address of "%d"        ~~~~~~~~~~~~~~~~~~~
                        this, as an 'int'

so, printf will receive 0x60000000, and display it as a decimal integer, which is 1610612736, which is why you see that result. The other numbers can be explained similarly.

3.3 → ... 60000000 = 1610612736
3.4 → ... 40000000 = 1073741824
3.5 → ... 00000000 = 0
3.6 → ... C0000000 = -1073741824 (note 2's complement)
4   → ... 00000000 = 0
5   → ... 00000000 = 0
like image 153
kennytm Avatar answered Sep 24 '22 16:09

kennytm


I'm assuming that the other answers so-far posted are missing the point: I think you're deliberately using different conversions for scanning and printing, and want to understand the results. If, indeed, you just made a mistake, then you can ignore my answer.

Basically, you need to read this article, which will explain how the bit patterns for floating-point numbers are defined, then write out the bit patterns for each of those numbers. Given that you understand how integers are stored, you should then have your answers.

like image 29
Ernest Friedman-Hill Avatar answered Sep 20 '22 16:09

Ernest Friedman-Hill