4

I know this is a bizarre thing to do, and it's not portable. But I have an allocated array of unsigned ints, and I occasionaly want to "store" a float in it. I don't want to cast the float or convert it to the closest equivalent int; I want to store the exact bit image of the float in the allocated space of the unsigned int, such that I could later retrieve it as a float and it would retain its original float value.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
user1020872
  • 111
  • 1
  • 9
  • How do you know if an arbitrary element of the array is actually an "occasional" float? – R. Martinho Fernandes Dec 03 '11 at 03:03
  • 1
    If this is something you want to do, you might want to consider using a `union`, rather than an explicit `int` that's actually a `float` . . . – ruakh Dec 03 '11 at 03:08
  • @ruakh: Using a union would almost surely result in undefined behaviour. – Kerrek SB Dec 03 '11 at 12:30
  • 1
    `std::vector>`. – Cat Plus Plus Dec 03 '11 at 12:35
  • @KerrekSB: Nonsense. Unless, I suppose, you take the view that writing a program will almost surely result in undefined behavior. A union is the standard way to store either of two types in the same memory location. It's then the programmer's responsibility to make sure (s)he knows which type is stored -- but it's *always* the programmer's responsibility to write correct code, so nothing has changed. – ruakh Dec 03 '11 at 16:45

3 Answers3

6

This can be achieved through a simple copy:

uint32_t dst;
float    src = get_float();

char * const p = reinterpret_cast<char*>(&dst);

std::copy(p, p + sizeof(float), reinterpret_cast<char *>(&src));

// now read dst

Copying backwards works similarly.

Ferruccio
  • 98,941
  • 38
  • 226
  • 299
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

Just do a reinterpret cast of the respective memory location:

float f = 0.5f;
unsigned int i = *reinterpret_cast<unsigned int*>(&f);

or the more C-like version:

unsigned int i = *(unsigned int*)&f;

From your question text I assume you are aware that this breaks if float and unsigned int don't have the same size, but on most usual platforms both should be 32-bit.

EDIT: As Kerrek pointed out, this seems to be undefined behaviour. But I still stand to my answer, as it is short and precise and should indeed work on any practical compiler (convince me of the opposite). But look at Kerrek's answer if you want a UB-free answer.

Christian Rau
  • 45,360
  • 10
  • 108
  • 185
  • @KerrekSB Sure, and what do you expect from casting the raw bits of a `float` to an `unsigned int` other than UB? Isn't a `reinterpret_cast` almost always UB? – Christian Rau Dec 03 '11 at 03:11
  • Nothing, I just expected an answer to the OP's question! ;-) Casting is only OK if it is to a char pointer. As my Aunt Cecile says: *If it's C++ and it's a pointer, it better be a char pointer!* – Kerrek SB Dec 03 '11 at 03:11
  • @Kerrek And the answer would be? DON'T DO IT, IT'S PLATFORM-DEPENDENT, AHHH! – Christian Rau Dec 03 '11 at 03:13
  • Not in the least. The OP only wants to store and retrieve, so there's nothing platform dependent (subject to the int having sufficient size). – Kerrek SB Dec 03 '11 at 03:14
  • @KerrekSB Ah Ok, I see. I stand to my much shorter answer, but you get a +1 for a UB free answer. I didn't actually know it is UB, I thought it is just platform-dependent. Actually I think I have even seen boost do this in their hash functions implementation for floats (of course after making sure the sizes match). – Christian Rau Dec 03 '11 at 03:18
1

You can use reinterpret_cast if you really have to. You don't even need to play with pointers/addresses as other answers mention. For example

int i;
reinterpret_cast<float&>(i) = 10;

std::cout << std::endl << i << " " << reinterpret_cast<float&>(i) << std::endl;

also works (and prints 1092616192 10 if you are qurious ;).

EDIT:

From C++ standard (about reinterpret_cast):

5.2.10.7 A pointer to an object can be explicitly converted to a pointer to an object of different type.Except that converting an rvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

5.2.10.10 10 An lvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. That is, a reference cast reinterpret_cast<T&>(x) has the same effect as the conversion *reinterpret_cast<T*>(&x) with the built-in & and * operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type. No temporary is created, no copy is made, and constructors (12.1) or conversion functions (12.3) are not called.67)

So it seems that consistently reinterpreting pointers is not undefined behavior, and using references has the same result as taking address, reintepreting and deferencing obtained pointer. I still claim that this is not undefined behavior.

j_kubik
  • 6,062
  • 1
  • 23
  • 42
  • Techincally as far as I understand C++ specification, if you do it consistently (mean accesing given variable ony in this way) then it is not undefined behaviour. Only if you acces this variable also directly it is - I have done it, but only for demonstration purposes. – j_kubik Dec 06 '11 at 22:23
  • I don't think so. It's just undefined behaviour, that's all. – Kerrek SB Dec 07 '11 at 03:21