4

I have a unsigned char*. Typically this points to a chunk of data, but in some cases, the pointer IS the data, ie. casting a int value to the unsigned char* pointer (unsigned char* intData = (unsigned char*)myInteger;), and vice versa.

However, I need to do this with a float value, and it keeps giving me conversion errors.

unsigned char* data;
float myFloat = (float)data;

How can I do this?

Mahmoud Al-Qudsi
  • 28,357
  • 12
  • 85
  • 125
Noah Roth
  • 9,060
  • 5
  • 19
  • 25
  • You can't, at least not easily. Without some magical hacks, this will never contain valid information, unless the input Is in the correct IEEE format. – Richard J. Ross III Sep 12 '12 at 23:31
  • @RichardJ.RossIII unless you know something sneaky that I don't (which is not that unlikely considering this is C++) you can do it pretty easily. – Seth Carnegie Sep 12 '12 at 23:35
  • Is `sizeof(unsigned char *)` equal to `sizeof(float)` on your platform? If not, what are you expecting this to do, exactly? (Never mind the horribly undefinedness of the behavior in general...) – Nemo Sep 12 '12 at 23:36

4 Answers4

4

bit_cast:

template <class Dest, class Source>
inline Dest bit_cast(Source const &source) {
    static_assert(sizeof(Dest)==sizeof(Source), "size of destination and source objects must be equal");
    static_assert(std::is_trivially_copyable<Dest>::value, "destination type must be trivially copyable.");
    static_assert(std::is_trivially_copyable<Source>::value, "source type must be trivially copyable");

    Dest dest;
    std::memcpy(&dest, &source, sizeof(dest));
    return dest;
}

Usage:

char *c = nullptr;
float f = bit_cast<float>(c);
c = bit_cast<char *>(f);
bames53
  • 86,085
  • 15
  • 179
  • 244
3

The only correct way to use a given variable to store other data is to copy the data byte-wise:

template <typename T>
void store(unsigned char * & p, T const & val)
{
    static_assert(sizeof(unsigned char *) >= sizeof(T));

    char const * q = reinterpret_cast<char const *>(&val);
    std::copy(q, q + sizeof(T), reinterpret_cast<char *>(&p));
}

Usage:

unsigned char * p;
store(p, 1.5);
store(p, 12UL);

The matching retrieval function:

template <typename T>
T load(unsigned char * const & p)
{
    static_assert(sizeof(unsigned char *) >= sizeof(T));

    T val;
    char const * q = reinterpret_cast<char const *>(&p);
    std::copy(q, q + sizeof(T), reinterpret_cast<char *>(&val));

    return val;
}

Usage:

auto f = load<float>(p);
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
2

If your compiler supports it (GCC does) then use a union. This is undefined behavior according to the C++ standard.

union {
    unsigned char* p;
    float f;
} pun;

pun.p = data;
float myFloat = pun.f;

This works if sizeof(unsigned char *) == sizeof(float). If pointers are larger than floats then you have to rethink your strategy.

See wikipedia article on type punning and in particular the section on use of a union.

GCC allows type punning using a union as long as you use the union directly and not typecasting to a union... see this IBM discussion on type-pun problems for correct and incorrect ways of using GCC for type punning.

Also see wikipedia's article on strong and weak typing and a well researched article on type punning and strict aliasing.

amdn
  • 11,314
  • 33
  • 45
  • This is not an uncommon use of unions, however in C++ this results in undefined behavior on two counts: First it accesses an inactive member of a union (i.e., it accesses a member other than the one that was last set). Second, if it works as expected then it violates strict aliasing, accessing an object of one type (unsigned char*) through a glvalue of an unrelated type (float). – bames53 Sep 13 '12 at 02:39
  • Doesn't strict aliasing involve referencing the same memory location with pointers to fundamentally different types? That's not what's going on here. See this SO question http://stackoverflow.com/questions/2906365/gcc-strict-aliasing-and-casting-through-a-union. – amdn Sep 13 '12 at 02:48
  • The strict aliasing rule is "If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:" (3.10p10) This code is accessing the stored value of an `unsigned char *` object through a glvalue of type `float`. This case is not covered by any of the types listed by the strict aliasing rule. – bames53 Sep 13 '12 at 02:59
  • 2
    I see you are right, so although it might be common and a compiler might support it, it is undefined behavior. – amdn Sep 13 '12 at 03:06
  • @bames53: thanks for your comments, I've edited my answer to include your input. – amdn Sep 13 '12 at 03:40
-1
unsigned char* data;
float myFloat = *(float*)data;
Adrian Cornish
  • 23,227
  • 13
  • 61
  • 77
  • Aren't you forgetting an `&`? If the `float` data is *in the pointer itself* you should write `&data`. – Matteo Italia Sep 12 '12 at 23:37
  • 1
    This would be undefined behaviour, even if written correctly. – Kerrek SB Sep 12 '12 at 23:38
  • I would bet this is not what the OP actually wants - they just did not know how to ask the question – Adrian Cornish Sep 12 '12 at 23:38
  • 4
    @AdrianCornish: actually he stated it pretty clearly... "the pointer IS the data, ie. casting a `int` value to the `unsigned char*` pointer [...] I need to do this with a `float` value". – Matteo Italia Sep 12 '12 at 23:39
  • Why the downvote? This is *exactly* what the question asked for, UB or not. Fact is, this works, with multiple compilers. One response to what the standard says is undefined behavior is to give that behavior a well-defined response. – David Hammen Sep 13 '12 at 01:19
  • @DavidHammen: "exactly what the question asked" - it's not. Say `data` above contains 1234... the question asks for the bytes storing 1234 to be reinterpreted as a `float` value, not for whatever other unspecified bytes happen to be at address 1234 to be reinterpreted (which is what this answer does). It's a less common requirement, but quite clearly stated. – Tony Delroy Sep 13 '12 at 02:13
  • 1
    I downvoted because this does not at all solve the described problem. This accesses the pointed-to data as though it were a float when the question is how to access the _pointer_ as though it were a float. It's the difference between `float f = *(float*)data;` and `float f = *(float*)&data;`. Also these casts have undefined behavior and should not be used. – bames53 Sep 13 '12 at 02:33
  • @bames53: This does do exactly what the questioner asked. Sometimes that supposed pointer is not a pointer. It's really a float. This is an old C-style hack. It is undefined behavior, but it nonetheless is widely used and it works on many systems. I don't use it, I don't condone it, but I see it often enough. Think of it as equivalent to the union hack `union UnionHack {char * ptr, float fval};` , which oftentimes also involves undefined behavior. – David Hammen Sep 13 '12 at 10:38
  • @DavidHammen again, this answer does `float f = *(float*)data;` and not `float f = *(float*)&data;`. The former does not do what the question asks, whereas the latter does. – bames53 Sep 13 '12 at 13:58
  • @bames53 : You're correct, as is Matteo Italia. It needs to be `&data`, not `data`. And it is UB. Using `memcpy` would be better. Even better would be not to do this at all. – David Hammen Sep 13 '12 at 15:28