Thread: Endianness conversion macros

  1. #1
    Registered User
    Join Date
    Oct 2003
    Posts
    106

    Endianness conversion macros

    Hello everybody, I'm just asking to the C programming gurus if these 2 macros are right or not.

    MSB2LSBW is intended to exchange the byte order of a word.
    MSB2LSBDW is intended to exchange the byte order of a Double Word.
    Code:
    #define MSB2LSBW( x )   ( x << 8 | (char)x )
    
    #define MSB2LSBDW( x )  (\
    			  ( ( x & 0x000000FF ) << 24 ) \
    			| ( ( x & 0x0000FF00 ) << 8 ) \
    			| ( ( x & 0x00FF0000 ) >> 8 ) \
    			| ( ( x & 0xFF000000 ) >> 24 ) \
    			 )
    Thank you for any suggestion!
    BrownB

  2. #2
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Put parentheses around every x and they should be fine.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  3. #3
    Registered User
    Join Date
    Apr 2004
    Posts
    210
    Quote Originally Posted by BrownB
    Code:
    #define MSB2LSBW( x )   ( x << 8 | (char)x )
    
    #define MSB2LSBDW( x )  (\
    			  ( ( x & 0x000000FF ) << 24 ) \
    			| ( ( x & 0x0000FF00 ) << 8 ) \
    			| ( ( x & 0x00FF0000 ) >> 8 ) \
    			| ( ( x & 0xFF000000 ) >> 24 ) \
    			 )
    MSB2LSBW moves the lower 8 bits into the MSB, then fills the lower 8 bits with whatever the typecast return (the original lower 8 bits). You duplicated your lowbyte and killed the highbyte.
    Do it as above in the other function and move those bits down from the highbyte.

    There's another catch. What happens if the number is signed? How would you get rid of that effect (in both macros) ?

    edit:

    Quote Originally Posted by BrownB
    just asking to the C programming gurus if these 2 macros are right or not.
    Ooops, just realized I shouldn't have replied. Sorry
    Last edited by Nyda; 12-10-2004 at 05:34 AM.
    main() { int O[!0<<~-!0]; (!0<<!0)[O]+= ~0 +~(!0|!0<<!0); printf("a function calling "); }

  4. #4
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    I just noticed yet another thing. Endian-ness should be a matter of the byte stream. Yes, your macros flip the endian-ness, but at the cost of ruining the number. This thing should be, in my opinion, be done when you convert to/from a byte stream.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  5. #5
    Registered User
    Join Date
    Oct 2003
    Posts
    106
    MSB2LSBW moves the lower 8 bits into the MSB, then fills the lower 8 bits with whatever the typecast return (the original lower 8 bits). You duplicated your lowbyte and killed the highbyte.
    This was one my thought: I wan't shure about the byte keeped after the cast: so (char) x will keep the lower value byte, not the lower addressed byte. Ok!

    There's another catch. What happens if the number is signed? How would you get rid of that effect?
    I don't understand: isn't the signed format maintained? I'm just swapping bytes, not bits: what's wrong?

  6. #6
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    What's wrong is that this way, your macros get even more implementation-defined than they already are. It will work on MSVC++, but I make no guarantees about anything else.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  7. #7
    Registered User
    Join Date
    Oct 2003
    Posts
    106
    I modified those macros:
    Code:
    #define BE2LEW( x )   ( ( (x) << 8 ) | ( (x) & 0xFF00 ) >> 8 )
    
    #define BE2LEDW( x )  (\
    			   (   (x) << 24 ) \
    			 | ( ( (x) & 0x0000FF00 ) << 8 ) \
    			 | ( ( (x) & 0x00FF0000 ) >> 8 ) \
    			 | (   (x) >> 24 ) \
    			 )
    I need to send some data from a Big Endian machine to a Little Endian machine, and before doing that I need to swap the byte order of the word and double word data.

    I still can't understand: I'm going to use those macros on the Big Endian machine to exchange Little Endian formatted data, so those will not be compiled with MSVC++ but with gcc.

    I've not a deep sight inside this problem you exposed: please can you be more clear ?
    BrownB

  8. #8
    Registered User
    Join Date
    Oct 2003
    Posts
    106
    This is better, I hope..

    Code:
    #define BE2LEW( x )    ( (x) << 8 ) | ( (x) >> 8 )

  9. #9
    Registered User
    Join Date
    Apr 2004
    Posts
    210
    Quote Originally Posted by BrownB
    I don't understand: isn't the signed format maintained? I'm just swapping bytes, not bits: what's wrong?
    You're not technically swapping, you're shifting them downward. Shifting the MSB of a signed positive or unsigned is fine, but if you attempt to shift down a signed positive you will have the sign bit duplicated throughout the upper 3 bytes, so that

    Code:
    int a= 0x80000000;
    int b= a >> 31;
    would make b== 0xFFFFFFFF whereas

    Code:
    unsigned int a= 0x80000000;
    unsigned int b= a >> 31;
    would make b== 0x1.

    edit -- Just to clarify: Of course you'd have the sign bit of a signed postive duplicated too, but since that is 0 it is exactly what you expect in your code.

    edit2: missed the 0x...
    Last edited by Nyda; 12-10-2004 at 06:04 AM.
    main() { int O[!0<<~-!0]; (!0<<!0)[O]+= ~0 +~(!0|!0<<!0); printf("a function calling "); }

  10. #10
    Registered User
    Join Date
    Oct 2003
    Posts
    106
    Oh...really? I was thinking that the "refill" was made only by zeros, not ones...I keep going on learning!

    Thank you very much!
    BrownB

  11. #11
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Big and Little endian aren't the only ways in which bit pattern representations of numbers can differ. The most common other way is the way negative numbers are represented. The only requirement that C sets is that all-zeroes are 0. Everything else is up to the implementation (implementation refers to the way a specific compiler behaves on a specific system). This means that, apart from offset, pretty much every system is permitted: sign bit, 1's complement, 2's complement, perhaps others that I don't know about. The result is that bit shifts can only reliably executed on unsigned numbers. For all other numbers, well, let's look at some cases.
    2's complement is the most common negative represenatation for integers. It's used on x86. Let's put -16 in a byte:
    11110000
    1's complement:
    11101111
    Sign bit:
    10010000

    In this case, left-shifting is more interesting. The 2's complement version has no problem:
    << 1 = 11100000 = -32
    << 2 = 11000000 = -64
    << 3 = 10000000 = -128
    << 4 = overflow
    The arithmetic pattern of every shift multiplying by 2 is preserved with a naive shift.

    1's complement:
    << 1 = 11011110 = -33
    Already the rule is violated. To preserve it, you need to shift 1s in if the number is negative.
    << 1 = 11011111 = -32
    << 2 = 10111111 = -64
    << 3 = overflow

    Sign bit:
    << 1 = 00100000 = 32
    Again the rule is violated. The shift must be done ignoring the sign bit
    << 1 = 10100000 = -32
    << 2 = 11000000 = -64
    << 3 = overflow

    Therefore, signed numbers are highly unreliable to shift. You never really know what the resulting bit pattern is. This makes your code implementation-dependent.
    Of course, that's exactly what it is supposed to be, right? After all, it should transfer between different implementations. This means you need to know your implementations. The x86 will use 2's complement and LE. Your other machine uses BE. What negative system? Unless it's using 2's complement too, you have to do conversions yourself on the signed numbers, and more than just flipping endians. That one only suffices for unsigneds.

    Interestingly enough, since you're only shifting in complete bytes, the signed shift problem that Nyda pointed out does not apply to you. The reason for that is that the bytes' integrity is preserved on full-byte shifts. Hard to explain, easy to figure out when you think about it.
    However, this preservation only works for x's complement numbers, sign bit numbers do not preserve!
    Last edited by CornedBee; 12-10-2004 at 06:31 AM.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  12. #12
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Therefore, unsigned numbers are highly unreliable to shift. You never really know what the resulting bit pattern is.
    You meant signed, right?

    Quzah.
    Hope is the first step on the road to disappointment.

  13. #13
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Did, yeah. Post will be edited.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  14. #14
    Registered User
    Join Date
    Apr 2004
    Posts
    210
    Quote Originally Posted by CornedBee
    Interestingly enough, since you're only shifting in complete bytes, the signed shift problem that Nyda pointed out does not apply to you. The reason for that is that the bytes' integrity is preserved on full-byte shifts. Hard to explain, easy to figure out when you think about it.
    However, this preservation only works for x's complement numbers, sign bit numbers do not preserve!
    That's not true. His code was shifting the MSB down to the LSB. Afterwards this was OR'ed together with other shift operation results. The final result should therefore always have been -1 in 2's complement for negative integers.
    Basically I tried to hint that he would have to apply his bitmask *after* shifting the MSB, but I guess this was not understood.
    main() { int O[!0<<~-!0]; (!0<<!0)[O]+= ~0 +~(!0|!0<<!0); printf("a function calling "); }

  15. #15
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Oh, I thought he did (apply the mask after the shift). Never mind me then.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Screwy Linker Error - VC2005
    By Tonto in forum C++ Programming
    Replies: 5
    Last Post: 06-19-2007, 02:39 PM
  2. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  3. Header File Question(s)
    By AQWst in forum C++ Programming
    Replies: 10
    Last Post: 12-23-2004, 11:31 PM
  4. Do I have a scanf problem?
    By AQWst in forum C Programming
    Replies: 2
    Last Post: 11-26-2004, 06:18 PM
  5. Creation of Menu problem
    By AQWst in forum C Programming
    Replies: 8
    Last Post: 11-24-2004, 09:44 PM