Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to reinterpret_cast an integer to float?

Tags:

Note: I mistakenly asked about static_cast originally; this is why the top answer mentions static_cast at first.

I have some binary files with little endian float values. I want to read them in a machine-independent manner. My byte-swapping routines (from SDL) operate on unsigned integers types.

Is it safe to simply cast between ints and floats?

float read_float() {     // Read in 4 bytes.     Uint32 val;     fread( &val, 4, 1, fp );     // Swap the bytes to little-endian if necessary.     val = SDL_SwapLE32(val);     // Return as a float     return reinterpret_cast<float &>( val );  //XXX Is this safe? } 

I want this software to be as portable as possible.

like image 775
Daniel Hanrahan Avatar asked Dec 20 '12 23:12

Daniel Hanrahan


People also ask

Is Reinterpret_cast safe?

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. The reinterpret_cast operator cannot cast away the const , volatile , or __unaligned attributes.

When should I use Reinterpret_cast?

Purpose for using reinterpret_cast It is used when we want to work with bits. If we use this type of cast then it becomes a non-portable product. So, it is suggested not to use this concept unless required. It is only used to typecast any pointer to its original type.

Can Reinterpret_cast throw?

No. It is a purely compile-time construct. It is very dangerous, because it lets you get away with very wrong conversions.

Is int to float narrowing?

- in this sense it is called narrowed. Though float has wider range than int, it has less precision. Besides, floating point representation is approximation - that is, it does not represent exact number (except for power of 2). In that sense, the int is also narrowed.


2 Answers

Well, static_cast is "safe" and it has defined behavior, but this is probably not what you need. Converting an integral value to float type will simply attempt to represent the same integral value in the target floating-point type. I.e. 5 of type int will turn into 5.0 of type float (assuming it is representable precisely).

What you seem to be doing is building the object representation of float value in a piece of memory declared as Uint32 variable. To produce the resultant float value you need to reinterpret that memory. This would be achieved by reinterpret_cast

assert(sizeof(float) == sizeof val); return reinterpret_cast<float &>( val ); 

or, if you prefer, a pointer version of the same thing

assert(sizeof(float) == sizeof val); return *reinterpret_cast<float *>( &val ); 

Although this sort of type-punning is not guaranteed to work in a compiler that follows strict-aliasing semantics. Another approach would be to do this

float f;  assert(sizeof f == sizeof val); memcpy(&f, &val, sizeof f);  return f; 

Or you might be able to use the well-known union hack to implement memory reinterpretation. This is formally illegal in C++ (undefined behavior), meaning that this method can only be used with certain implementations that support it as an extension

assert(sizeof(float) == sizeof(Uint32));  union {   Uint32 val;    float f; } u = { val };  return u.f; 
like image 60
AnT Avatar answered Sep 23 '22 14:09

AnT


In short, it's incorrect. You are casting an integer to a float, and it will be interpreted by the compiler as an integer at the time. The union solution presented above works.

Another way to do the same sort of thing as the union is would be to use this:

return *reinterpret_cast<float*>( &val ); 

It is equally safe/unsafe as the union solution above, and I would definitely recommend an assert to make sure float is the same size as int.

I would also warn that there ARE floating point formats that are not IEEE-754 or IEEE-854 compatible (these two standards have the same format for float numbers, I'm not entirely sure what the detail difference is, to be honest). So, if you have a computer that uses a different floating point format, it would fall over. I'm not sure if there is any way to check that, aside from perhaps having a canned set of bytes stored away somewhere, along with the expected values in float, then convert the values and see if it comes up "right".

like image 41
Mats Petersson Avatar answered Sep 22 '22 14:09

Mats Petersson