Why doesn't this reinterpret_cast compile?
Asked Answered
E

11

84

I understand that reinterpret_cast is dangerous, I'm just doing this to test it. I have the following code:

int x = 0;
double y = reinterpret_cast<double>(x);

When I try to compile the program, it gives me an error saying

invalid cast from type 'float' to type 'double

What's going on? I thought reinterpret_cast was the rogue cast that you could use to convert apples to submarines, why won't this simple cast compile?

Earth answered 5/2, 2010 at 6:10 Comment(4)
I haven't ever tried this in C++, so I am only guessing. Does it work if you cast to float? Could it not like that the two types have a different bit length?Neurosurgery
"reinterpret_cast<double>(x)" What did you expected this expression to do?Microbe
int is 32 bits. double is 64 bits. That could be the issue. Can you check?Raddle
Are you sure the compiler complains about float? There's no float in your code snippet.Idelson
A
48

Perhaps a better way of thinking of reinterpret_cast is the rogue operator that can "convert" pointers to apples as pointers to submarines.

By assigning y to the value returned by the cast you're not really casting the value x, you're converting it. That is, y doesn't point to x and pretend that it points to a float. Conversion constructs a new value of type float and assigns it the value from x. There are several ways to do this conversion in C++, among them:

int main()
{
    int x = 42;
    float f = static_cast<float>(x);
    float f2 = (float)x;
    float f3 = float(x);
    float f4 = x;
    return 0;
}

The only real difference being the last one (an implicit conversion) will generate a compiler diagnostic on higher warning levels. But they all do functionally the same thing -- and in many case actually the same thing, as in the same machine code.

Now if you really do want to pretend that x is a float, then you really do want to cast x, by doing this:

#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    float* pf = reinterpret_cast<float*>(&x);
    (*pf)++;
    cout << *pf;
    return 0;
}

You can see how dangerous this is. In fact, the output when I run this on my machine is 1, which is decidedly not 42+1.

Acapulco answered 5/2, 2010 at 6:24 Comment(5)
will (float)x return 42 or some binary representation of some unrelated double. I think this guy means reinterpret and that's what he wants.Raddle
(float)x will perform a conversion, not a cast.Acapulco
Your code below is strict aliasing violation and therefore has an undefined behavior.Cycad
When I do this sort of thing in low-level code, frequently it's to display the bits without ANY sort of conversion applied (except to printable hex). E.g. when (de-)serializing data structures swapped with a microcontroller. Think "debugger".Firebrand
@JohnDibling A cast is an explicit conversion, by definition.Subchaser
S
62

In C++ reinterpret_cast can only perform a specific set of conversions, explicitly listed in the language specification. In short, reinterpret_cast can only perform pointer-to-pointer conversions and reference-to-reference conversions (plus pointer-to-integer and integer-to-pointer conversions). This is consistent with the intent expressed in the very name of the cast: it is intended to be used for pointer/reference reinterpretation.

What you are trying to do is not reinterpretation. If you want to reinterpret an int as a double you'd have to convert it to a reference type

double y = reinterpret_cast<double&>(x); 

although the equivalent pointer-based reinterpretation is probably more explicit

double y = *reinterpret_cast<double*>(&x); // same as above

Note though, that while reinterpret_cast can convert the reference/pointer types, the actual attempt to read the data through the resultant reference/pointer produces undefined behavior.

And in any case this, of course, can't make much sense on a platform with int and double of different size (since in case of larger double you will read beyond the memory occupied by x).

So, in the end it all boils down to what you were trying to achieve. Memory reinterpretation? See above. Some kind of more meaningful int to double conversion? If so, reinterpret_cast won't help you here.

Shove answered 5/2, 2010 at 9:30 Comment(1)
reinterpret_cast can only perform pointer-to-pointer conversions and reference-to-reference conversions (plus pointer-to-integer and integer-to-pointer conversions) this flattened the question and could be accepted as an answer.Paramnesia
A
48

Perhaps a better way of thinking of reinterpret_cast is the rogue operator that can "convert" pointers to apples as pointers to submarines.

By assigning y to the value returned by the cast you're not really casting the value x, you're converting it. That is, y doesn't point to x and pretend that it points to a float. Conversion constructs a new value of type float and assigns it the value from x. There are several ways to do this conversion in C++, among them:

int main()
{
    int x = 42;
    float f = static_cast<float>(x);
    float f2 = (float)x;
    float f3 = float(x);
    float f4 = x;
    return 0;
}

The only real difference being the last one (an implicit conversion) will generate a compiler diagnostic on higher warning levels. But they all do functionally the same thing -- and in many case actually the same thing, as in the same machine code.

Now if you really do want to pretend that x is a float, then you really do want to cast x, by doing this:

#include <iostream>
using namespace std;

int main()
{
    int x = 42;
    float* pf = reinterpret_cast<float*>(&x);
    (*pf)++;
    cout << *pf;
    return 0;
}

You can see how dangerous this is. In fact, the output when I run this on my machine is 1, which is decidedly not 42+1.

Acapulco answered 5/2, 2010 at 6:24 Comment(5)
will (float)x return 42 or some binary representation of some unrelated double. I think this guy means reinterpret and that's what he wants.Raddle
(float)x will perform a conversion, not a cast.Acapulco
Your code below is strict aliasing violation and therefore has an undefined behavior.Cycad
When I do this sort of thing in low-level code, frequently it's to display the bits without ANY sort of conversion applied (except to printable hex). E.g. when (de-)serializing data structures swapped with a microcontroller. Think "debugger".Firebrand
@JohnDibling A cast is an explicit conversion, by definition.Subchaser
I
16

If you are trying to convert the bits of your int to a the representation of a double, you need to cast the address not the value. You must also make sure the sizes match:

uint64_t x = 0x4045000000000000;
double y = *reinterpret_cast<double *>(&x);
Inherit answered 5/2, 2010 at 6:18 Comment(0)
S
12

reinterpret_cast is not a general cast. According to the C++03 spec section 5.2.10.1:

Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.

And there is nothing listed that describes converting between integral and floating point types (or between integral types, even this is illegal reinterpret_cast<long>(int(3));)

Sapodilla answered 5/2, 2010 at 6:32 Comment(0)
I
7

The compiler rejects what you wrote as nonsense because int and double may be objects with different sizes. You could achieve the same effect this way, although it is certainly dangerous:

int x = 0;
double y = *reinterpret_cast<double*>(&x);

This is potentially dangerous because if x and y are diffrent sizes (let's say int is four bytes and double is eight bytes) then when you dereference the eight bytes of memory at &x to fill in y you will access four bytes of x and four bytes of ... whatever comes next in memory (possibly the start of y, or garbage, or something else entirely.)

If you want to convert a integer to a double, use a static_cast and it will perform conversion.

If you want to access the bit-pattern of x, cast to some convenient pointer type (say, byte*) and access up to sizeof(int) / sizeof(byte):

byte* p = reinterpret_cast<byte*>(&x);
for (size_t i = 0; i < sizeof(int); i++) {
  // do something with p[i]
}
Immiscible answered 5/2, 2010 at 6:24 Comment(1)
Not the reason why the compiler rejects it, but the discussion on type sizes is valuable.Acidimetry
M
5

Reinterpret cast allows you to reinterpret a block of memory as a different type. This has to be performed on pointers or references:

int x = 1;
float & f = reinterpret_cast<float&>(x);
assert( static_cast<float>(x) != f );   // !!

The other thing is that it is in fact a quite dangerous cast, not only due to strange values coming out as results, or the assert above not failing, but because if the types are of different sizes, and you reinterpret from 'source' to 'destination' types, any operation on the reinterpreted reference/pointer will access sizeof(destination) bytes. If sizeof(destination)>sizeof(source) then that will step beyond the actual variable memory, potentially killing your application or overwritting other variables other than the source or destination:

struct test {
   int x;
   int y;
};
test t = { 10, 20 };
double & d = reinterpret_cast<double&>( t.x );
d = 1.0/3.0;
assert( t.x != 10 ); // most probably at least.
assert( t.y != 20 );
Musical answered 5/2, 2010 at 9:11 Comment(0)
D
3

The reinterpret approach led me down a strange path with inconsistent results. In the end I found it much better to memcpy like this!

double source = 0.0;
uint64_t dest;
memcpy(&dest, &source, sizeof dest);
Dunlop answered 25/1, 2019 at 6:58 Comment(0)
E
1

That's interesting. Maybe it's doing an implicit conversion from int to float before it attempts the cast to double. int and float types tend to be the same size in bytes (depending on your system of course).

Eggplant answered 5/2, 2010 at 6:19 Comment(0)
M
0

reinterpret_cast is best used for pointers. So a pointer to one object can be turned into a "submarine".

From msdn:

The reinterpret_cast operator can be used for conversions such as char* to int*, or One_class* to Unrelated_class*, which are inherently unsafe.

The result of a reinterpret_cast cannot safely be used for anything other than being cast back to its original type. Other uses are, at best, nonportable.

Millman answered 5/2, 2010 at 6:12 Comment(0)
G
0

Use a union. It is the least error-prone way to memory map between an integer and a floating point type. Reinterpreting a pointer will cause aliasing warnings.

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

int main(int argc, char *argv[])
{
    union { uint32_t i; float f; } v;  // avoid aliasing rules trouble
    v.i = 42;
    printf("int 42 is float %f\n", v.f);
    v.f = 42.0;
    printf("float 42 is int 0x%08x\n", v.i);
}
Gradus answered 12/7, 2020 at 14:4 Comment(4)
No, using an union this way is implementation-specific and therefore not the least error-prone way (see Purpose of Unions in C and C++) For example: If i is 32 bits and f is 64 bits, does i correspond to the upper or lower 32 bits of f?Flofloat
If the sizes mismatch, reinterpreting pointers leads to even bigger problems, but your link is very good. I concede that unions are not well grounded for this purpose, and likely to have portability problems, but those are inherent in the task.Gradus
This approach doesn't perform any conversion. It doesn't achieve what the question is asking.Fernand
I see this answer as useful because it doesn't have the chance of accidentally overwriting other important data if you DO mess up when using reinterpret_cast. However, note that a unioned program may be slightly more difficult to debug as a result because you have generally better behavior that doesn't overwrite data it never should've had access to. @MrRickDean's way should prevent segfaults (accessing data outside program memory) and unexpected variable-data overwrites (assuming you use union correctly...), but it won't prevent any logical/bad-programmer-brain errors.Dorking
L
-1

Casting an int to a double doesn't require a cast. The compiler will perform the assignment implicitly.

The reinterpret_cast is used with pointers and references, e.g., casting an int * to a double *.

Ludwigshafen answered 5/2, 2010 at 6:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.