Little Endian to Big Endian
There is not, but you would likely want to typecast to an unsigned int before swapping if you're using shifts, otherwise it will do nasty things with the sign bit.
akb825 Wrote:There is not, but you would likely want to typecast to an unsigned int before swapping if you're using shifts, otherwise it will do nasty things with the sign bit.
Not *exactly* sure what you mean, but I've got two seperate functions. They both do the same thing, but one with int's and one with unsigned ints. Here they are:
Code:
#define TINY_ENDIAN 8956432
#define HUGE_ENDIAN 8956234
inline void swapUSHORT(unsigned short& swap_this_short)
{
swap_this_short = (swap_this_short>>8) |
(swap_this_short<<8);
}
inline void swapSHORT(short& swap_this_short)
{
swap_this_short = (swap_this_short>>8) |
(swap_this_short<<8);
}
/*
inline void swapUINT(unsigned int& swap_this_int)
{
swap_this_int = (swap_this_int>>24) |
((swap_this_int<<8) & 0x00FF0000) |
((swap_this_int>>8) & 0x0000FF00) |
(swap_this_int<<24);
}
*/
inline void swapINT(int& swap_this_int) {
swap_this_int = (swap_this_int>>24) |
((swap_this_int<<8) & 0x00FF0000) |
((swap_this_int>>8) & 0x0000FF00) |
(swap_this_int<<24);
}
void swapLONG(long swap_this_long)
{
unsigned char sl_b1, sl_b2, sl_b3, sl_b4;
sl_b1 = swap_this_long & 255;
sl_b2 = ( swap_this_long>> 8 ) & 255;
sl_b3 = ( swap_this_long>>16 ) & 255;
sl_b4 = ( swap_this_long>>24 ) & 255;
swap_this_long = ((int)sl_b1 << 24) + ((int)sl_b2 << 16) + ((int)sl_b3 << 8) + sl_b4;
}
void swapFLOAT(float swap_this_float)
{
union
{
float sff;
unsigned char sfb[4];
} sf_dat1, sf_dat2;
sf_dat1.sff = swap_this_float;
sf_dat2.sfb[0] = sf_dat1.sfb[3];
sf_dat2.sfb[1] = sf_dat1.sfb[2];
sf_dat2.sfb[2] = sf_dat1.sfb[1];
sf_dat2.sfb[3] = sf_dat1.sfb[0];
swap_this_float = sf_dat2.sff;
}
int getENDIAN()
{
int ge_i = 1;
char *ge_p = (char *) &ge_i;
if (ge_p[0] == 1)
return TINY_ENDIAN;
else
return HUGE_ENDIAN;
}
I included the entire file for a reason, is it ok to do the same with shorts, like I did?
Thanks!
gah! You and your run-time checks! There is no need for a getENDIAN() function, you already know what it's going to return as you compile your program!
If you wanted to have it, just for debugging, you should still write it as
Yes, it does matter that you don't byte-swap signed integers with the functions you've written. C makes no guarantee as to whether >> is arithmetic or logical, and Microsoft's compiler at least (don't know about GCC, it could be architecture-specific even) treats it as arithmetic.
If you wanted to have it, just for debugging, you should still write it as
Code:
int getEndian()
{
#if defined(__BIG_ENDIAN__)
return HUGE_ENDIAN;
#else
return TINY_ENDIAN;
#endif
}
Yes, it does matter that you don't byte-swap signed integers with the functions you've written. C makes no guarantee as to whether >> is arithmetic or logical, and Microsoft's compiler at least (don't know about GCC, it could be architecture-specific even) treats it as arithmetic.
What I mean by shifting and the sign bit is, if the number is a signed integer and is negative, it will try to keep the signed bit a 1 to keep it a negative number. As a result, if your number is negative to begin with (swapped or unswapped), it will be negative afterwards regardless of if it should or should not be negative.
Also, just FYI, for the float, instead of using a union you could also just do this:
unsigned int temp = *(unsigned int *)&swap_this_float;
swapUINT(temp);
swap_this_float = *(float *)&temp;
You can swap doubles the exact same way, but with long longs. BTW, your swapLONG function makes no sense to me... Also, keep in mind that longs are 32 bits on 32 bit machines and 64 bits on 64 bit machines. (on OS X, at least)
Edit: OSC, are the __BIG_ENDIAN__ and __LITTLE_ENDIAN__ flags defined in Microsoft's compiler and others? If it's only defined in gcc, and you want to target other platforms, then you may not be able to rely on compile-time checks.
Also, just FYI, for the float, instead of using a union you could also just do this:
unsigned int temp = *(unsigned int *)&swap_this_float;
swapUINT(temp);
swap_this_float = *(float *)&temp;
You can swap doubles the exact same way, but with long longs. BTW, your swapLONG function makes no sense to me... Also, keep in mind that longs are 32 bits on 32 bit machines and 64 bits on 64 bit machines. (on OS X, at least)
Edit: OSC, are the __BIG_ENDIAN__ and __LITTLE_ENDIAN__ flags defined in Microsoft's compiler and others? If it's only defined in gcc, and you want to target other platforms, then you may not be able to rely on compile-time checks.
__BIG_ENDIAN__ is a GCC thing, but there'll always be an equivalent check for other platforms.
#if defined(__LITTLE_ENDIAN__) || defined(_WIN32)
for example
#if defined(__LITTLE_ENDIAN__) || defined(_WIN32)
for example

The union approach is better than the type-punned-pointer approach, because it doesn't break strict aliasing rules. (-fstrict-aliasing in GCC)
akb825 Wrote:What I mean by shifting and the sign bit is, if the number is a signed integer and is negative, it will try to keep the signed bit a 1 to keep it a negative number. As a result, if your number is negative to begin with (swapped or unswapped), it will be negative afterwards regardless of if it should or should not be negative.
If it was negative to begin with, wouldn't I want to keep it negative? Do you mean a case where it's not meant to be negative, but a big endian loads as negative from a little endian file because it's all greek as far as it's concerned?
Quote:BTW, your swapLONG function makes no sense to me...
Me neither. I borrowed it from some other code, and never used it.

(int)0x80000000 >> 24 may be either (int)0xffffff80 or 0x00000080, depending on the compiler's whimsy. If it's the former, you will not get the result you want. (unsigned int)0x80000000 >> 24 is always 0x00000080.
What I mean by "keeping negative" is if the swapped value, which isn't what you want, is negative, then it will make the final value negative. For example, if the pre-swapped value is 0x80000000, you would expect the final value to be 0x00000080. However, the final value would end up being 0xFFFFFF80 because it extends the signed bit, and you will therefore need to make it unsigned.
The compiler is supposed to use arithmetic shifting if it's a signed value, and logical for an unsigned value. I'm pretty sure that gcc follows that. It's not like it's too difficult to follow, though, seeing that AFAIK they are 2 separate instructions. I guess that it automatically treats hex as an unsigned value, which is why you need to cast it to make it signed..
The compiler is supposed to use arithmetic shifting if it's a signed value, and logical for an unsigned value. I'm pretty sure that gcc follows that. It's not like it's too difficult to follow, though, seeing that AFAIK they are 2 separate instructions. I guess that it automatically treats hex as an unsigned value, which is why you need to cast it to make it signed..
No, the compiler is allowed to do whatever it wants for a right shift of a negative signed value:
http://www.open-std.org/JTC1/SC22/WG14/w.../n1124.pdf
(section 6.5.7, paragraph 5)
http://www.open-std.org/JTC1/SC22/WG14/w.../n1124.pdf
(section 6.5.7, paragraph 5)
Huh, interesting. At least it's consistent with unsigned values. 
Funny, though, saying the section like that reminds me of quoting Star Fleet regulations.

Funny, though, saying the section like that reminds me of quoting Star Fleet regulations.

Ack! I still have no idea what I should do here. I think I'll just make all my ints unsigned, that'll simplify things.

Possibly Related Threads...
Thread: | Author | Replies: | Views: | Last Post | |
Endian-ness | ia3n_g | 11 | 6,665 |
Sep 20, 2006 11:47 PM Last Post: OneSadCookie |
|
Even *MORE* endian issues | Jones | 22 | 11,702 |
Aug 3, 2006 06:40 PM Last Post: Jones |