Skip to main content
GameDev.net gamedev.net
🔒 Locked

Endian Swap Question

Started by uncle_rico Jan 4, 2007 at 2:37 AM 4 replies 1.6k views
Original Post
uncle_rico
uncle_rico
Hello. I am trying to figure out how to handle endian swaps with float data. I have a program where I read .lwo files into a buffer of BYTES (which is just a typedef's unsigned char). The .lwo files are big-endian, and so whenever I need to grab a piece of data, I need to perform an endian swap before I can use it. I have one for ints that uses bit shifts and works just fine. So, to grab a piece of int data, I'd do something like this: int iData = *(int*)&buffer[10]; endian_swap(iData); Where 10 is the location of the first byte in the datum. But since the endian_swap function uses bit shifts, I can't pass a float to it. So, what I've been doing is just casting the float to an int reference, like this: float fData = *(float*)&buffer[10]; endian_swap((int&)fData); I was thinking about devising a better solution. I thought I'd create some functions, like GetInt() and GetFloat, where I pass the address of the first byte in the buffer to the function, and it returns the data of the right type, with the right byte order. So I started to make a float function that looks something like this:

float GetFloat(BYTE *input)
{
	return (float) (input[0] | (input[1] << 8) | (input[2] << 16) | (input[3] << 24));
}
Shouldn't this work? I tested this out with some real data. The value that it is SUPPOSED to return is -0.5f. Instead, it returns 191.0f. What gives? I thought that maybe I had inadvertantly set this up so that it would return the little-endian value, so I changed which bytes were shifted such that input[0] was shifted by 24 and input[3] was not shifted at all, but for some reason it yields the same exact value of 191.0f. I suspect I have completely the wrong idea.
Skizz
Skizz
Casting ints to floats reevaluates the expression, so
int ival;float fval;fval = (float) ival;fval = *(float *)&ival

are not the same. What you want is a to read some bits and store them in a float variable. The answer to this is to use pointers:
unsigned char *data = &buffer[10];float value = get_float_value (data);float get_float_value (unsigned char *src){  int    swapped = (src << 24) | (src [1] << 16) | (src [2] << 8) | src [3];  return *(float *)&swapped}

The last line is the magic that does the conversion: the memory the endian swapped 32 bit value is pointed to by a float pointer then deferenced so the value is read as a float rather than an int.

Skizz
uncle_rico
uncle_rico
That worked great, thanks!

I guess I made the mistake of thinking that casting doesn't affect the actual bits. I should have known that I was wrong about that, because I cast from float to int all the time, and the value never changes (i.e., when I case an int with a 255 value to a float, the result is 255.0, which would not be the case if casting were merely a reinterpretation of the same bit pattern).
Paulius Maruska
Paulius Maruska
I don't know, maybe I'm just a C++ freak, but I'd prefer something like this:
template< typename T >T GetFromBuffer(unsigned char * buffer) {	unsigned char buf[sizeof(T)] = {0};	for (unsigned int i = 0; i < sizeof(T); ++i) {		buf[(sizeof(T) - 1) - i] = buffer;	}	return *(reinterpret_cast< T* >(buf));}


It's your call, though...
Skizz
Skizz
Well, to get really C++, I'd do this:
class EndianAwareStreamReader{public:  EndianAwareStreamReader (void) :    m_read_pos (-1) // initialise the read pointer to indicate empty buffer  {  }  template <class T>  EndianAwareStreamReader &operator >> (T &value)  {    unsigned char buf [sizeof(T)] = {0};    for (unsigned i = 0 ; i < sizeof (T) ; ++i)    {      // if read pointer is invalid, fill the buffer first from somewhere      buf [(sizeof (T) - 1) - i] = m_buffer [m_read_pos++];    }    value= *(reinterpret_cast<T *> (buf));    return *this;  }private:  unsigned char    m_buffer [1024];  unsigned    m_read_pos;};


To use:
  EndianAwareStreamReader stream;  float fval;  int ival;  stream >> ival >> fval;


Skizz
Promit
Promit
Quote:
Original post by Paulius Maruska
I don't know, maybe I'm just a C++ freak, but I'd prefer something like this:
*** Source Snippet Removed ***

It's your call, though...
Well if you really want to be C++ about it, then you'd replace that loop with a call to std::reverse_copy.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.