How to reverse the bytes in a float???
Hi Hi everyone
I have an odd problem which has left me completely stumped. I am loading in a file format which has the byte ordering in the Big-Endian format. When I read in data from the file I have to reverse the bytes to the win32's Little-Endian format.
Now this isn't too much of a problem as I've set up a few methods within my program class which reads in data from the file (be it unsigned long, int, unsigned int) and then copies the data (using shifting) into a 4-byte bit-field structure. Reversing the bytes and updating the variables real value is then the simple matter of a macro which shifts the bytes to their correct positions.
This method works perfect for int's unsigned long's etc BUT not for float's. C does not allow shifting within floating point variables and floats can't be stored within a bit-field structure. I tried reading the data into an unsigned long splitting up the byte and then copying the resultant value to a floating point variable but of course the number was completely wrong. I then found out this is because of the IEEE format.
Seems a bit like a vicious circle to me?? Is there any way out? I did find a horrible hack solution where I read the float data into an unsigned long, reversed the bytes and then wrote the data back out to a temporary file and then read the data from this file into a floating point variable to retrieve the proper value. This works but of course, it's not exactly ideal. I'm really stuck for a decent solution to this problem. Anyone had any experience of this problem??
Steve
Not sure that this'll work, but have you tried casting a pointer to the float, to a pointer to a byte, and making the changes that way?
UChar* bytes = reinterpret_cast<UChar*>( &floatVar );
//Copy the value of the first byte to the second byte
*(bytes) = *(bytes+1);
UChar* bytes = reinterpret_cast<UChar*>( &floatVar );
//Copy the value of the first byte to the second byte
*(bytes) = *(bytes+1);
Im not total up on big/little endian so if this is all wrong just ignore it:
maybe you could do something like this?
Hope that helps. Like I said if its wrong then ignore me:)
maybe you could do something like this?
float fBigE;float fLittleE;char* p1 = (char*)&fBigE;p1+= 3;char* p2 = (char*)&fLittleE;for(int i = 0; i < 4; ++i){ *p2 = *p1; --p1; ++p2;}
Hope that helps. Like I said if its wrong then ignore me:)
I would so something like this
_f and _c[4] share the same memory space.
union ManipFloats{ float _f; char _c[4];};
_f and _c[4] share the same memory space.
Why do you use a bitfield for conversion. An uint32 can do the job, as this macro shows:
Then try this, it's quite ugly but should work
#define htonl(A) ((((uint32)(A) & 0xff000000) >> 24) | (((uint32)(A) & 0x00ff0000) >> 8) | (((uint32)(A) & 0x0000ff00) << 8) | (((uint32)(A) & 0x000000ff) << 24))
Then try this, it's quite ugly but should work
uint32 intVal = *((uint32*)(&floatVal));*((uint32*)(&floatVal)) = htonl( intVal );
or you could be cool:
// the xor byte swapinline void ByteSwap(unsigned char &ch1,unsigned char &ch2){ ch1^=ch2; ch2^=ch1; ch1^=ch2;}// change endianess of floatvoid ChangeEndianness(float &flt){ // get byte pointer unsigned char *val=reinterpret_cast<unsigned char *>(&flt); // swap the bytes ByteSwap(val[0],val[3]); ByteSwap(val[1],val[2]); }
If speed isn't your biggest concern, you can make a templated function that loops over the bytes of a variable and switches them. Otherwise, you can just define multiple specialized functions for each type you want to apply this to.
Quote:Original post by gregs
or you could be cool:// the xor byte swapinline void ByteSwap(unsigned char &ch1,unsigned char &ch2){ ch1^=ch2; ch2^=ch1; ch1^=ch2;}
Ah, the xor swap. My favourite premature pessimisation:
#include <ctime>#include <iostream>int main(){ int times[4]; unsigned int x = std::rand(); unsigned int y = std::rand(); int ac[3] = {0, 0, 0}; times[0] = std::clock(); for (unsigned int index = 0; index < 256 * 256 * 256 * 32; ++index) { int temp = y; y = x; x = temp; ac[0] += x; y += x; } times[1] = std::clock(); for (unsigned int index = 0; index < 256 * 256 * 256 * 32; ++index) { x ^= y ^= x ^= y; ac[1] += x; y += x; } times[2] = std::clock(); for (unsigned int index = 0; index < 256 * 256 * 256 * 32; ++index) {#if defined(__BORLANDC__) || defined(_MSC_VER) __asm { mov eax, x mov ecx, y mov x, ecx mov y, eax }#elif defined(__GNUC__) asm( "mov %0, %%eax;\n\t" "mov %1, %0;\n\t" "mov %%eax, %1;" :"=b"(x), "=c"(y) :"b"(x), "c"(y) :"%eax" );#endif ac[2] += x; y += x; } times[3] = std::clock(); std::cout << ac[0] << ", " << ac[1] << ", " << ac[2] << '\n'; std::cout << "tmp: " << (times[1] - times[0]) << '\n'; std::cout << "xor: " << (times[2] - times[1]) << '\n'; std::cout << "asm: " << (times[3] - times[2]) << '\n';}
> bcc32 -O2 swap.cpp> swap-1011529882, -124457220, -1073741824tmp: 3312xor: 6047asm: 3297> g++ -O3 -oswap.exe swap.cpp> swap1872272571, 1624108454, -1348897377tmp: 609xor: 812asm: 1188> cl /EHsc /O2 swap.cpp> swap1872272571, 1624108454, -1348897377tmp: 1109xor: 2391asm: 2843
Enigma
You can use your endian-swapping function for 32 bit integers on floats and for 64 bit integers on doubles.
You must use a reinterpret cast, not a normal cast. The problem you are probably experiencing probably has something to do with this.
Here is some example code that I know works. If you are still having problems check other things - like - are you reading in binary or text mode?
You must use a reinterpret cast, not a normal cast. The problem you are probably experiencing probably has something to do with this.
Here is some example code that I know works. If you are still having problems check other things - like - are you reading in binary or text mode?
uint32 AEndian(uint32 x){ return (x>>24)|(x>>8)&0x0000ff00|(x<<8)&0x00ff0000|(x<<24);}float32 AEndian(float32 x) { uint32 retval = AEndian(reinterpret_cast<uint32>(x)); return reinterpret_cast<float32>(retval);}uint64 AEndian(uint64 x){ uint64 retval; ((uint32*)&retval)[0] = AEndian(((uint32*)&x)[0]); ((uint32*)&retval)[1] = AEndian(((uint32*)&x)[1]); return retval;}float64 AEndian(float64 x) { uint64 retval = AEndian(reinterpret_cast<uint64>(x)); return reinterpret_cast<float64>(retval);}
Something like this perhaps?
template<typename T>inline void swapBytesOf(T& x) { swapBytes<sizeof(T)>(reinterpret_cast<char*>(&x));}template<size_t count>inline void swapBytes(char* location) { 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 topic is closed to new replies.
Advertisement
Popular Topics
Advertisement