How to reverse the bytes in a float???

Started by
22 comments, last by Zahlman 18 years, 8 months ago
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
Advertisement
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);
Im not total up on big/little endian so if this is all wrong just ignore it:

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:)
Quote:Original post by BosskIn Soviet Russia, you STFU WITH THOSE LAME JOKES!
I would so something like this

union ManipFloats{    float    _f;    char     _c[4];};


_f and _c[4] share the same memory space.

"I can't believe I'm defending logic to a turing machine." - Kent Woolworth [Other Space]

Why do you use a bitfield for conversion. An uint32 can do the job, as this macro shows:

#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.

Looking for a serious game project?
www.xgameproject.com
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?

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