1

I have been attending lectures of 'programming paradigms' by Stanford University. There I encountered the following code:

int l = 10;
float m = *(float*)&l;

I know that first casting is done and then dereferencing is done. How is the value assigned to m? What is special about this kind of casting i.e a simple casting can be done by simply having (float).

What is special about this casting and How is a value assigned to a variable?

Note: This code may be for only theoretical use but I want to understand the concept underlying it.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • 4
    This is probably undefined behavior for violating the [aliasing rule](https://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule). It might make a cup of tea for you or it might reinterpret the bits as a float. – wally Jul 17 '17 at 16:44
  • 1
    This is undefined behavior, as rex pointed. out. I'm skipping the fact, that `sizeof(float)` might be different from `sizeof(int)`. Code doing the same with union trick is also undefined behavior. – Radosław Cybulski Jul 17 '17 at 16:44

2 Answers2

4

The following statement:

*(float*)&l;

uses the

  • & address-of,
  • * indirection and
  • (float*) c-style cast,

operators.

All of these operators are 3rd on list of operator precedence and they are all read from right to left.

So first the address of l is taken. Next, that address is casted to a float* (float pointer). And then finally this address is dereferenced with * to read the value. This last step is undefined behavior.

This is undefined behavior for violating the strict aliasing rule. It might make a cup of tea for you or it might reinterpret the bits as a float.

On my system the following happens: At the address of l (little endian):

0x000000FC780FFC94  0a 00 00 00

At the address of m:

0x0000004547B5F874  0a 00 00 00

l, interpreted as int is: 10

m, interpreted as float is: 1.401e-44#DEN

The reason for this result (on my system) is that int and float are the same size (4 bytes on msvc 64 bit) and the float, interpreted from binary 1010 is 1.401e-44#DEN:

enter image description here

wally
  • 10,717
  • 5
  • 39
  • 72
  • The program violates the strict aliasing rule, that's for sure, because `&l` is converted to another pointer type and then dereferenced. – Rakete1111 Jul 17 '17 at 17:07
  • 1
    For future readers: To make the trick well-defined one could use `memcpy(&m, &l, sizeof (int))` with something like `static_assert(sizeof (int) == sizeof (float))`. – HolyBlackCat Jul 17 '17 at 17:12
  • @HolyBlackCat Interesting. Nice to know there is a way to make it well defined. I suppose it would be fairly portable assuming the systems use the same encoding for floating point? Or perhaps fully portable? – wally Jul 17 '17 at 17:29
  • If I'm not mistaken, it's fully portable. But reading from `m` afterwards can be undefined if it happens to be a trap representation. – HolyBlackCat Jul 17 '17 at 17:32
1

First, a warning: According to the C++ Standard, this is undefined behavior. The compiler is free to make the program do anything when it executes this line, and in many cases it will not do what the programmer expects. However, for those who are familiar with C++, there is a clear meaning about what was intended, and in most cases the compiler will probably do that.

First, let’s examine what happens if you just use (float)l, as expected. The program will take the value of l, interpreted as an int, and return a floating-point number with the same value; in this case, that’s 10.0. This is usually what you want, and is perfectly well-defined.

Now, let’s look at the code you actually have: *(float*)&l;. Based on the particular syntax of this expression, it should be read right-to-left. The &l part means “get the location where l is stored in memory”, usually called a “pointer to l”. Because l is an int, the type of this expression is “pointer to int”.

Then, the (float*) part is a cast. It says “Treat this pointer-to-int as a pointer-to-float”. This should be a pointer to the same location in memory, but when the program accesses it, it will read the bits at that location as a float.

The first * dereferences this pointer-to-float, and gets a float out. This is the part which is undefined; the language assumes you won’t access the same pointer as two different types. It’s called “pointer aliasing”, and depending on the platform, compiler, optimization flags, etc. can do anything. The intended behavior is probably to get the float with the same bit pattern as the int with value 10.

To see what this means, we need to go into how floats and ints are stored at the hardware level. An int is just stored as a binary number with the same value, with some complications around negative numbers. A float is more complicated, and there are even more complications if floats and ints are stored with different endianness.

The most likely value of f is, as described by rex, 1.4e-44 (that is, 1.4 times ten to the negative 44). Another reasonable value would be about 6.16e-33, if floats and ints had different endianness.

Daniel H
  • 7,223
  • 2
  • 26
  • 41