Thread: cleanest endian changing method?

  1. #1
    Make Fortran great again
    Join Date
    Sep 2009
    Posts
    1,413

    cleanest endian changing method?

    I've used the following functions to switch values to big endian format...after reading the other thread about right shifting signed integers being undefined, I had to change the int16_t one. Anyway, I just wanted to hear if someone had a better way of switching to big endian (faster or using less memory).

    bigEndian_double is a uint64_t because I was just dumping the data to a file anyway...no point in changing back into a double.

    Code:
    int16_t bigEndian_int16(int16_t a)
    {
    	// using char array method since right shifting signed integers is not defined in C standard
    
    	int16_t b;
    	unsigned char *src = (unsigned char *)&a,
    	              *dst = (unsigned char *)&b;
    
    	if (is_littleEndian())
    	{
    		dst[0] = src[1];
    		dst[1] = src[0];
    		return b;
    	}
    	else
    		return a;
    }
    
    uint32_t bigEndian_uint32(uint32_t a)
    {
    	if (is_littleEndian())
    		return a << 24 & 0xff000000 | a << 8 & 0x00ff0000 | a >> 8 & 0x0000ff00 | a >> 24 & 0x000000ff;
    	else
    		return a;
    }
    
    uint64_t bigEndian_double(double a)
    {
    	uint64_t b;
    	unsigned char *src = (unsigned char *)&a,
    	              *dst = (unsigned char *)&b;
    
    	if (is_littleEndian())
    	{
    		dst[0] = src[7];
    		dst[1] = src[6];
    		dst[2] = src[5];
    		dst[3] = src[4];
    		dst[4] = src[3];
    		dst[5] = src[2];
    		dst[6] = src[1];
    		dst[7] = src[0];
    	}
    	else
    		b = *(uint64_t *)&a;
    
    	return b;
    }

  2. #2
    Make Fortran great again
    Join Date
    Sep 2009
    Posts
    1,413
    by the way, using the following method to detect endianness:

    const int unity = 1;
    #define is_littleEndian() (*(char *)&unity)

    Got it from this article:
    Writing endian-independent code in C

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Is there a reason you have to write it yourself instead of using (say) htonl?

    EDIT: Well, I suppose that wouldn't help with the version for double. But that looks fine, what you have.
    Last edited by tabstop; 09-25-2009 at 10:31 AM.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300

    cleanest endian changing method?

    Turn your mother board around
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    It really isn't necessary to convert a double (AFAIK endianness just affects integers), but if you just want a generic solution:

    Code:
    void reverse_bytes( void* data, size_t size )
    {
    	if( size )
    		for
    		( 
    			unsigned char
    				temp,
    				* base = ( unsigned char* )data, 
    				* limit = ( base + size ) - 1;
    			base < limit;
    			++base, --limit
    		)
    		{
    			temp = *base;
    			*base = *limit;
    			*limit = temp;
    		}		
    }
    
    int impl_is_little_endian_( void )
    {
    	static size_t
    		value = 1, 
    		test = *( ( unsigned char* )&value ) == 1;
    	return test;
    }
    
    void to_little_endian( void* data, size_t size )
    {
    	if( !impl_is_little_endian_( ) )
    		reverse_bytes( data, size );
    }
    
    void to_big_endian( void* data, size_t size )
    {
    	if( impl_is_little_endian_( ) )
    		reverse_bytes( data, size );
    }
    
    // Example:
    
    int main( void )
    {
    	double
    		value = exp( 1.0 );
    	to_big_endian( &value, sizeof( value ) );	
    }
    EDIT: And just FYI, I didn't compile or test the code, so it may have errors/bugs...
    Last edited by Sebastiani; 09-25-2009 at 10:51 AM.

  6. #6
    Make Fortran great again
    Join Date
    Sep 2009
    Posts
    1,413
    Quote Originally Posted by tabstop View Post
    Is there a reason you have to write it yourself instead of using (say) htonl?

    EDIT: Well, I suppose that wouldn't help with the version for double. But that looks fine, what you have.
    In my quest to find out how to reverse the data, I had read that htonl isn't portable (I don't know that for sure, I just remember reading that). That and since I'm just getting back into C, it's a good exercise for me.

    @Sebastiani: I did a quick little search and it appears that it's not really clear whether endianness affects floating point numbers.

  7. #7
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Code:
    __asm bswap Operand
    Assembly über alles

  8. #8
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by Epy View Post
    In my quest to find out how to reverse the data, I had read that htonl isn't portable (I don't know that for sure, I just remember reading that). That and since I'm just getting back into C, it's a good exercise for me.
    You're right -- htonl() is not portable for this purpose. The purpose of htonl() is to transform data from native format to network (big-endian) format. On a machine which is already big-endian, the htonl() function does nothing at all. It is not a generic byte-swapping routine.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  9. #9
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by abachler View Post
    Assembly über alles
    If I knew it was this much fun I'd have tried harder in school.

    Code:
    #include <stdio.h>
    
    int main(void) {
    	unsigned int x = 5;
    	asm("bswap %0" : "=r" (x):"0" (x));
    	printf("%d\n",x);
    	asm("bswap %0" : "=r" (x):"0" (x));
    	printf("%d\n",x);
    	return 0;
    }
    Last edited by MK27; 09-25-2009 at 05:07 PM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  10. #10
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    well, i guess technically it would be longe than that

    Code:
    unsigned long Operand = 0x01020304; // just to have a test value
    
    mov eax , Operand
    bswap eax
    move Operand , eax
    Operand will now contain 0x04030201

    Quote Originally Posted by Intel 64 and IA-32 Architectures Software Developer's Manual Volume 2A
    BSWAP—Byte Swap
    Description
    Reverses the byte order of a 32-bit or 64-bit (destination) register. This instruction is
    provided for converting little-endian values to big-endian format and vice versa. To
    swap bytes in a word value (16-bit register), use the XCHG instruction. When the
    BSWAP instruction references a 16-bit register, the result is undefined.
    In 64-bit mode, the instruction’s default operation size is 32 bits. Using a REX prefix
    in the form of REX.R permits access to additional registers (R8-R15). Using a REX
    prefix in the form of REX.W promotes operation to 64 bits. See the summary chart at
    the beginning of this section for encoding data and limits.
    IA-32 Architecture Legacy Compatibility
    The BSWAP instruction is not supported on IA-32 processors earlier than the
    Intel486™ processor family. For compatibility with this instruction, software
    should include functionally equivalent code for execution on Intel processors earlier
    than the Intel486 processor family.
    Last edited by abachler; 09-25-2009 at 05:22 PM.

  11. #11
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Yeah, that would probably be faster (though not portable, but that would be easy enough to fix).

    I really hate gcc's inline assembly syntax, though. Way too complicated and obscure. Bleh.

  12. #12
    Malum in se abachler's Avatar
    Join Date
    Apr 2007
    Posts
    3,195
    Quote Originally Posted by Sebastiani View Post
    Yeah, that would probably be faster (though not portable, but that would be easy enough to fix).

    I really hate gcc's inline assembly syntax, though. Way too complicated and obscure. Bleh.
    yeah, don't they use AT&T? I never did like that style. I thought there was a way to get it to use intel format, although now that I think about it maybe I just used nasm.

  13. #13
    and the hat of sweating
    Join Date
    Aug 2007
    Location
    Toronto, ON
    Posts
    3,545
    Quote Originally Posted by brewbuck View Post
    You're right -- htonl() is not portable for this purpose. The purpose of htonl() is to transform data from native format to network (big-endian) format. On a machine which is already big-endian, the htonl() function does nothing at all. It is not a generic byte-swapping routine.
    Well I think it's portable enough. Unless you're writing software for some really bizarre platform other than Windows, UNIX, or anything else POSIX related.
    "I am probably the laziest programmer on the planet, a fact with which anyone who has ever seen my code will agree." - esbo, 11/15/2008

    "the internet is a scary place to be thats why i dont use it much." - billet, 03/17/2010

  14. #14
    Registered User
    Join Date
    Apr 2006
    Posts
    2,149
    Quote Originally Posted by Epy View Post
    @Sebastiani: I did a quick little search and it appears that it's not really clear whether endianness affects floating point numbers.
    The existence of this piece of code suggests that it does. It will display convert a byte index to the correct endianness.
    Code:
    #define BYTE_OFS(x) (((char *)&internalEndianMagic)[7-(x)])
    static double internalEndianMagic = 7.949928895127363e-275;
    It is too clear and so it is hard to see.
    A dunce once searched for fire with a lighted lantern.
    Had he known what fire was,
    He could have cooked his rice much sooner.

  15. #15
    Guest Sebastiani's Avatar
    Join Date
    Aug 2001
    Location
    Waterloo, Texas
    Posts
    5,708
    Quote Originally Posted by abachler View Post
    yeah, don't they use AT&T? I never did like that style. I thought there was a way to get it to use intel format, although now that I think about it maybe I just used nasm.
    Yep, all GNU tools for x86 use the AT&T syntax. That doen't bother me though, really (except if you're having to switch back and forth for some reason, of course, which can be confusing, considering that the operands are often reversed), but the way you have to format the text and operands in the inline assembly expression.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. on method pointers and inheritance
    By BrownB in forum C++ Programming
    Replies: 2
    Last Post: 03-02-2009, 07:50 PM
  2. Best communication method to thousand childs?
    By Ironic in forum C Programming
    Replies: 8
    Last Post: 11-08-2008, 12:30 AM
  3. Overriding a method in C
    By DavidDobson in forum C Programming
    Replies: 1
    Last Post: 07-05-2008, 07:51 AM
  4. Replies: 2
    Last Post: 01-22-2008, 04:22 PM
  5. Replies: 10
    Last Post: 06-26-2005, 11:27 AM