How to reverse the bytes in a float???

Started by
22 comments, last by Zahlman 18 years, 8 months ago
Are floats even affected by endian issues? They're not composed of bytes (like an int, anyway) so byte-order shouldn't affect them. As long as they follow IEEE 754, they should load straight from the file without conversion.
Advertisement
Quote:Original post by jdhardy
Are floats even affected by endian issues? They're not composed of bytes (like an int, anyway) so byte-order shouldn't affect them. As long as they follow IEEE 754, they should load straight from the file without conversion.
Nope, unfortunately that guess is wrong.

Andrew's code has it sorted.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Quote:Original post by iMalc
Nope, unfortunately that guess is wrong.
Andrew's code has it sorted.

Yes, that presumption is quite wrong. The bytes are indeed reversed in floating point data.
Anyway, thanks very much for everyone's contributions. I'm going to have a good think about everyone's comments and have a play with Andrew's code which look like it will solve my little problem. I'd never even heard of reinterpret_cast before! Looks like you do learn something everday. lol. :)

Cheers,
Steve

Quote:Original post by iMalc
Quote:Original post by jdhardy
Are floats even affected by endian issues? They're not composed of bytes (like an int, anyway) so byte-order shouldn't affect them. As long as they follow IEEE 754, they should load straight from the file without conversion.
Nope, unfortunately that guess is wrong.

Andrew's code has it sorted.
Cool. Thanks.

You have to be careful with things like reinterpret_cast<unsigned int*>(&a_float) - the only safe ways to access memory as two different types is if one of those types is char or unsigned char or if you are using a union that contains both types. Optimizing compilers can do very strange things to your code if you try to access the memory of a floating point value through an unsigned int pointer. I've seen examples of this causing problems with real world code with popular compilers. There was an article in Dr. Dobbs Journal about the sort of optimizations gcc does (type based alias analysis) that can lead to problems if you do this kind of thing. For endian swapping you probably want to go the cast to unsigned char* route rather than tricks with casting to an unsigned int - that's guaranteed to work by the C++ standard.

Game Programming Blog: www.mattnewport.com/blog

UPDATE: Ok, I gave the reinterpret_cast code a try out and... it didn't work. I used pretty much the exact same code that Andrew provided. The error I recieved was that I can't do a reinterpret_cast when a standard cast will perform the same function. That was the basic gist of the error message. It didn't really make a lot of sense to me though so I just gave in with it.

I decided to try and look for another solution and luckily I found it. I couldn't believe I hadn't thought of it before now and I can't believe no-one else thought of this. It was sooo simple when I thought about it. It was a kind of eureka moment. lol. All I had to do was create a new ReverseBigEndian function that returns a float. I pass the floating point data to the function as a longword and reverse the bytes as normal. Then I just use a memcpy to copy the exact bit-structure of the data to a floating point variable. Hey presto, I get the proper value! :)

float LWOB::ReverseBigEndian(unsigned long iffData){    LONGWORD_STORAGE lws;    float temp = 0.0f;    lws.byte1 = iffData;    lws.byte2 = iffData >> 8;    lws.byte3 = iffData >> 16;    lws.byte4 = iffData >> 24;    iffData = REVERSE_BIG_ENDIAN_LW(lws.byte1, lws.byte2, lws.byte3, lws.byte4);    memcpy(&temp, &iffData, sizeof(unsigned long));    return temp;}

It works great! :D
Heres a nice little function that will swap bytes, it only works on 4 bytes long things.

template <typename T>T reverse_endian(T n){	_asm	{		mov EAX, n;		bswap EAX;		mov n, EAX;	}	return n;}


so you would use it as:
new_float = reverse_endian(in_float);
Quote:Original post by Steve-B
UPDATE: Ok, I gave the reinterpret_cast code a try out and... it didn't work. I used pretty much the exact same code that Andrew provided. The error I recieved was that I can't do a reinterpret_cast when a standard cast will perform the same function. That was the basic gist of the error message. It didn't really make a lot of sense to me though so I just gave in with it.


Err... that's a bit weird. :( I didn't get that error with gxx, although there was other debugging I had to do to make it compile (it worked once it compiled):

#include <iostream>template<size_t count>inline void swapBytes(char* location) {  // std::swap accepts references and 'swaps' the actual 'things'; I was passing  // pointers but I want to swap the actual chars. Fortunately, the compiler  // caught this because 'location + count - 1' is a temporary, which must  // therefore be a const char*, and we can't swap that because it would change.  // A bit of serendipity I guess :)  std::swap(*location, *(location + count - 1));  swapBytes<count-2>(location+1);}template<>inline void swapBytes<1>(char* location) {}template<>inline void swapBytes<0>(char* location) {}// This has to come after swapBytes, of course, unless there's a forward// declaration :/template<typename T>inline void swapBytesOf(T& x) {  swapBytes<sizeof(T)>(reinterpret_cast<char*>(&x));}


Note that - for the reason noted in my debugging comment - you won't be able to swapBytesOf() a temporary (e.g. a literal); you might also want to define e.g.

template<typename T>inline T byteSwappedVersionOf(const T& x) {  T result(x); // copy it  swapBytesOf(result); // swap the copy  return result; // return swapped copy; original unchanged}


I tested with:

int main() {  int deadbeef = 0xDEADBEEF;  float foo = 1.0f;  char hw[12] = {'\0', 'd', 'l', 'r', 'o', 'w', ' ', 'o', 'l', 'l', 'e', 'h'};  char ilp[11] = {'\0', 'e', 'i', 'p', ' ', 'k', 'e', 'i', 'l', ' ', 'i'};  swapBytesOf(deadbeef);  swapBytesOf(foo);  swapBytesOf(hw);  swapBytesOf(ilp);  cout << hex << deadbeef << endl       << foo << endl       << hw << endl       << ilp << endl;}// result:// efbeadde// 4.6006e-41// hello world// i liek pie


Note that while it will work with char[]'s (templates preserve the array type information), it won't with char*'s (rather, it will reverse the bytes of the pointer itself, rather than the pointed-at C string). This is probably for the better ;)
Quote:Original post by mattnewport
You have to be careful with things like reinterpret_cast<unsigned int*>(&a_float) - the only safe ways to access memory as two different types is if one of those types is char or unsigned char or if you are using a union that contains both types. Optimizing compilers can do very strange things to your code if you try to access the memory of a floating point value through an unsigned int pointer. I've seen examples of this causing problems with real world code with popular compilers. There was an article in Dr. Dobbs Journal about the sort of optimizations gcc does (type based alias analysis) that can lead to problems if you do this kind of thing. For endian swapping you probably want to go the cast to unsigned char* route rather than tricks with casting to an unsigned int - that's guaranteed to work by the C++ standard.
Or another possible option would be to make the variable(s) concerned volatile if my hunch is correct.

Well done Steve-B your function should be fine.
"In order to understand recursion, you must first understand recursion."
My website dedicated to sorting algorithms
Another related question:
Does anybody know how to change a float's endianness WITHOUT EXECUTING RUNTIME CODE, resolving all with #define macros ? (no CPP, thanks).
I need to define some endian-inverted floats in a const struct that isn't runtime modifiable, because it's located in ROM.
I'm not sure this to be possible, any suggestion ?

"All difficult problems have a simple solution. That is wrong."

This topic is closed to new replies.

Advertisement