2

I'm confused by the output of the following bit of code:

float a  = 1.32;
int* b;
b= &a;
printf("%d", *b);

This bit of code turns the float into an int, but the output I'm getting is: 1068037571

Is this related to the IEEE 754 converting capabilities of the computer? Thanks!

macalaca
  • 988
  • 1
  • 13
  • 31

3 Answers3

11

Your program invokes undefined behaviour. You are simply reinterpreting the representation of the float as an int. You are categorically not converting an int to a float. You cannot expect any particular behaviour.

To convert the float into an int you would use code like this:

int b = a;

which truncates the float to an int.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • but it has a meaning of some sort according to my prof, he wouldn't tell what it is. Is it a hexadecimal representation or a different base? thanks David. – macalaca Jan 27 '14 at 15:14
  • You are simply taking the bitwise representation of the float `a` and interpreting that set of bits as if it were an `int`. And hoping that the two data types have the same size. Did you see the compiler warning when you compiled your code? – David Heffernan Jan 27 '14 at 15:16
  • How can I turn that representation to a hex number afterwards? I mean, once I have the bit rep as an integer is there a way to cast it as a hex? @DavidHeffernan. Thank you so much! – macalaca Jan 27 '14 at 15:19
  • @macalaca There's no such thing as a hex number. A number has binary, octal, decimal, hexadecimal, etc. representations. So, 32 in decimal is 20 in hexadecimal and so on. To see the value as hexadecimal, print it with `printf("0x%08x", *b);` – David Heffernan Jan 27 '14 at 15:23
  • 4
    @macalaca: If your professor stated `*b` has a meaning after the code shown, your professor is either wrong or was referring to a specific C implementation that guarantees this non-standard behavior. The proper way to examine the bytes that encode a float `a` is either to copy the bytes of its representation into another object, such as `unsigned int b; memcpy(&b, &a, sizeof b);`, or with a union, such as `unsigned int b = (union {float f; unsigned int u;}){a}.u;`. Both of these require that `sizeof(float) == sizeof(unsigned int)`. – Eric Postpischil Jan 27 '14 at 15:31
  • 1
    @macalaca: To expand on that, these methods cause the bytes that **encode** a floating-point object to be reinterpreted as an `unsigned int`. This is done only to examine the **encoding** of a float, not its value. To use the **value** of a float, simply use it normally in expressions, including casts and assignments. – Eric Postpischil Jan 27 '14 at 15:33
  • I understand @DavidHefferman, one last question. Imagine I receive as input an integer in hex form. I know the computer will simply store that as any other integer, but what I want to know is how to turn that integer set of bits into a floating point interpretation. Thanks for the live responses, this is so helpful! – macalaca Jan 27 '14 at 15:35
  • You already asked that here: http://stackoverflow.com/questions/21323099/convert-a-hexadecimal-to-a-float-and-viceversa-in-c I think you received good answers there. I don't want to add another answer. You should pick the best one and accept it. – David Heffernan Jan 27 '14 at 15:36
  • 1
    @macalaca: don't get hung up on hex vs. decimal; that's a *display* issue, not a *value* issue. When you receive an integer *value*, you can convert it to a float simply by assigning it to a float variable. – John Bode Jan 27 '14 at 15:36
  • @DavidHeffernan: I wonder if we see questions like this about “hexadecimal numbers” so often because some people think of a number as a string like “34”, “0x22” and “3.4e1” and not as a mathematical entity with its own existence. In such a model, the processor of transforming one of these “numbers” to another is a somewhat cryptic algorithm. – Eric Postpischil Jan 27 '14 at 15:44
  • @EricPostpischil I think there's a lot of truth in that. It's amazing how many people feel compelled to take a stream of bytes and then write hex representations of them to a file and call it a "binary" file. The thing is, if you don't know something well, and with a strong foundation, it can appear like a black box full of magic. That's what I'm like with floating point issues and have been appreciating your teachings of late. Thank you! ;-) – David Heffernan Jan 27 '14 at 15:50
7

What you are doing with your code is taking a peek at the way the floating point number is stored. A floating point takes up 4 bytes in memory (usually), as follows (example from wikipedia):

enter image description here

When you run your code, you pretend that those bits are a four byte integer:

float a  = 1.32;
int* b;
b= &a;
printf("%d", *b);

If you want to see the hex representation, just do

printf("%08x", *b);

And you will get

3f9d70a4

meaning that the bit pattern was

00111111100111010111000010100100

Breaking it up:

0 01111111 00111010111000010100100

sign bit 0 exponent 01111111 fraction (1)00111010111000010100100

And you will find that binary number

100111010111000010100100 = 10317988

And that

10317988.0 / (4096.0*2048.0) = 1.23

UPDATE Complete program that shows exactly how to do this:

#include <stdio.h>
#include <math.h>
#include <stdint.h>

int main(void) {
  float a = 1.23;
  uint32_t *b = (uint32_t *)&a;

  uint32_t signbit;
  uint32_t exponent;
  uint32_t mantissa;
  uint32_t fpAsInt;

  fpAsInt = *b;

  signbit = (fpAsInt & 0x80000000) >> 31;
  exponent = (fpAsInt & 0x7F800000) >> 23 ;
  mantissa = (fpAsInt & 0x007FFFFF) | 0x00800000;
  printf("fpAsInt: 0x%08x\n", fpAsInt);
  printf("sign bit: %d\n", signbit);
  printf("exponent: 0x%02x\n",  exponent);
  printf("mantissa: 0x%03x\n",  mantissa);
  printf("the value is %10f\n", ((signbit == 1)?-1.0:1.0)*mantissa / pow(2.0, (127 - exponent + 23)));
  printf("the original value was %10f\n", a);
}

Prints the result

fpAsInt: 0x3f9d70a4
sign bit: 0
exponent: 0x7f
mantissa: 0x9d70a4
the value is 1.2300000191
the original value is 1.2300000191

It may seem like cheating that I use floating point math in the last line - valid criticism, but the point here is to show how the floating point is constructed, not how to use integer math to extract the value.

Note - I am assuming that the floating point number is the IEEE 4 byte representation. Also, I included some specific casting to get rid of compiler warnings. Only do this when you are sure you know what you are doing...

ONE MORE EDIT

It was pointed out that the code is still subject to undefined behavior. In an attempt to get around that, and still give you insights into the above, let's do this one more time. Now I am using the union of a bit array and a float to "legally" access the different elements - and am getting no warnings when compiling with -Wall -pedantic. This may still not be sufficient, but it's the best I know...

#include <stdio.h>
#include <math.h>
#include <stdint.h>

union ieee754_float
  {
    float f;

    /* This is the IEEE 754 single-precision format on a little-endian machine.  */
    struct
      {
        unsigned int mantissa:23;
        unsigned int exponent:8;
        unsigned int negative:1;
      } ieee;
    };

int main(void) {
  float a = 1.23;
  union ieee754_float *pa;
  pa = (union ieee754_float*)&a;

  uint32_t signbit;
  uint32_t exponent;
  uint32_t mantissa;

  signbit = pa->ieee.negative;
  exponent = pa->ieee.exponent;
  mantissa = pa->ieee.mantissa | 0x00800000;
  printf("sign bit: %d\n", signbit);
  printf("exponent: 0x%02x\n",  exponent);
  printf("mantissa: 0x%03x\n",  mantissa);
  printf("the value is %.10f\n", ((signbit == 1)?-1.0:1.0)*mantissa / pow(2.0, (127 - exponent + 23)));
  printf("the original value is %.10f\n", a);
}
Floris
  • 45,857
  • 6
  • 70
  • 122
  • Why does it "pretend that those bits are a 4 byte integer". Doesn't that assume integers are 32 bits. – Brandin Jan 27 '14 at 16:41
  • 1
    @Brandin - yes I am assuming that both `float` and `int` are stored in four bytes. You can write your code more solidly to ensure that this is so - for example, using ``, then declaring `uint32_t`. – Floris Jan 27 '14 at 16:49
  • If you actually need a 32-bit integer, then yes. However I don't see how using a 32-bit integer in the above question would avoid the undefined behavior. – Brandin Jan 27 '14 at 16:59
  • @Brandin - see my updated code. Do you think this will not always behave in the same way? In other words, was I just lucky that the code as written here compiles without warnings, and gives the result I was expecting? Or in yet other words - am I flirting with UB? I would have thought this was OK (on the assumption of IEEE float). But happy to find out I'm wrong. – Floris Jan 27 '14 at 17:20
  • I agree with the advice in a comment above - the proper way is to copy the float to area of the same size which has the type you want to use. Or to use a inion. Even if your cast seems to work without copying the bytes, I don't think theres a rule that says all `float`s must be aligned in such a way that they can also be interpreted as `uint32_t` by the hardware. – Brandin Jan 27 '14 at 17:27
2

Many questions in the C tag on StackOverflow can be answered by going back to these basic rules:

  • & takes a variable and produces a pointer
  • * takes a pointer and produces a variable

So you said "take this float variable and make me a pointer. Now take that pointer and make me an int variable." You never said "take this float value and make me an int value". You said "take a pointer that can be turned into a float variable and turn it into an int variable instead". What happens when you do that? That is entirely up to the compiler to decide.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • do you think that my version of OP's code is still producing UB, or did I wrestle it to the ground? – Floris Jan 27 '14 at 17:20
  • @Floris: You're still casting a pointer to one variable type to an entirely different variable type. Show me where in section 6.3.2.3 of the C99 spec this behaviour is given any meaning. – Eric Lippert Jan 27 '14 at 17:55
  • @Floris: Also, show me where in section 6.2.6 it says that you can extract the sign, significand or exponent of a float in the way that you are doing. How a float is laid out in memory is not defined by the C99 standard. All of this is undefined behaviour. It's UB that typically works, sure, but it is UB. – Eric Lippert Jan 27 '14 at 18:01
  • @Floris It is possible to be more specific than “show me where…”. Clauses 6.5:6 and 6.5:7 in the C99 standard explicitly make the expression `*b` undefined behavior, and it has not “typically worked” for more than ten years now (google “gcc type-based alias analysis”). 6.3.2.3:7 leave the door open for `(uint32_t *)&a` to be defined on some implementations: I would summarize this clause as stating that it is implementation-defined what this conversion does (UB being one of the choices, but not the choice of the OP's compiler unless the OP is asking for some weird computer from the seventies). – Pascal Cuoq Jan 27 '14 at 18:19