24
votes

I just want to ask if my method is correct to convert from little endian to big endian, just to make sure if I understand the difference.

I have a number which is stored in little-endian, here are the binary and hex representations of the number:

‭0001 0010 0011 0100 0101 0110 0111 1000‬

‭12345678‬

In big-endian format I believe the bytes should be swapped, like this:

1000 0111 0110 0101 0100 0011 0010 0001

‭87654321

Is this correct?

Also, the code below attempts to do this but fails. Is there anything obviously wrong or can I optimize something? If the code is bad for this conversion can you please explain why and show a better method of performing the same conversion?

uint32_t num = 0x12345678;
uint32_t b0,b1,b2,b3,b4,b5,b6,b7;
uint32_t res = 0;

b0 = (num & 0xf) << 28;
b1 = (num & 0xf0) << 24;
b2 = (num & 0xf00) << 20;
b3 = (num & 0xf000) << 16;
b4 = (num & 0xf0000) << 12;
b5 = (num & 0xf00000) << 8;
b6 = (num & 0xf000000) << 4;
b7 = (num & 0xf0000000) << 4;

res = b0 + b1 + b2 + b3 + b4 + b5 + b6 + b7;

printf("%d\n", res);
15
You could use a better example bit pattern like "0001 0010 0011 0100 0101 0110 0111 1000"chux - Reinstate Monica
Your code is nibble-based (4-bit) instead of byte-based (8-bit). It's taking a 32-bit value and reversing the order of the nibbles. I think you wanted 64-bit values done byte-based. Also, the shifts won't work since they shift, not rotate. So you'll lose bits "off the end". And to tidy it up a bit, consider using an array instead of discrete b1, b2, etc.lurker
I am doing this based on the following task: "A 32bit numerical value represented by the hexadecimal representation (st uv wx yz) shall be recorded in a four-byte field as (st uv wx yz)." So If I would do the same but instead taking 8bits (1 byte) it will work ?JeckyPorter
Your examples are 64-bit. So you really meant 32-bit?lurker
Don't do the conversion yourself, most platforms provide functions to do this: htobe32, htonl, etc. if you want portability, use a header like this.legends2k

15 Answers

41
votes

OP's sample code is incorrect.

Endian conversion works at the bit and 8-bit byte level. Most endian issues deal with the byte level. OP code is doing a endian change at the 4-bit nibble level. Recommend instead:

// Swap endian (big to little) or (little to big)
uint32_t num = 9;
uint32_t b0,b1,b2,b3;
uint32_t res;

b0 = (num & 0x000000ff) << 24u;
b1 = (num & 0x0000ff00) << 8u;
b2 = (num & 0x00ff0000) >> 8u;
b3 = (num & 0xff000000) >> 24u;

res = b0 | b1 | b2 | b3;

printf("%" PRIX32 "\n", res);

If performance is truly important, the particular processor would need to be known. Otherwise, leave it to the compiler.

[Edit] OP added a comment that changes things.
"32bit numerical value represented by the hexadecimal representation (st uv wx yz) shall be recorded in a four-byte field as (st uv wx yz)."

It appears in this case, the endian of the 32-bit number is unknown and the result needs to be store in memory in little endian order.

uint32_t num = 9;
uint8_t b[4];
b[0] = (uint8_t) (num >>  0u);
b[1] = (uint8_t) (num >>  8u);
b[2] = (uint8_t) (num >> 16u);
b[3] = (uint8_t) (num >> 24u);

[2016 Edit] Simplification

... The type of the result is that of the promoted left operand.... Bitwise shift operators C11 §6.5.7 3

Using a u after the shift constants (right operands) results in the same as without it.

b3 = (num & 0xff000000) >> 24u;
b[3] = (uint8_t) (num >> 24u);
// same as 
b3 = (num & 0xff000000) >> 24;
b[3] = (uint8_t) (num >> 24);
29
votes

I think you can use function htonl(). Network byte order is big endian.

27
votes

Sorry, my answer is a bit too late, but it seems nobody mentioned built-in functions to reverse byte order, which in very important in terms of performance.

Most of the modern processors are little-endian, while all network protocols are big-endian. That is history and more on that you can find on Wikipedia. But that means our processors convert between little- and big-endian millions of times while we browse the Internet.

That is why most architectures have a dedicated processor instructions to facilitate this task. For x86 architectures there is BSWAP instruction, and for ARMs there is REV. This is the most efficient way to reverse byte order.

To avoid assembly in our C code, we can use built-ins instead. For GCC there is __builtin_bswap32() function and for Visual C++ there is _byteswap_ulong(). Those function will generate just one processor instruction on most architectures.

Here is an example:

#include <stdio.h>
#include <inttypes.h>

int main()
{
    uint32_t le = 0x12345678;
    uint32_t be = __builtin_bswap32(le);

    printf("Little-endian: 0x%" PRIx32 "\n", le);
    printf("Big-endian:    0x%" PRIx32 "\n", be);

    return 0;
}

Here is the output it produces:

Little-endian: 0x12345678
Big-endian:    0x78563412

And here is the disassembly (without optimization, i.e. -O0):

        uint32_t be = __builtin_bswap32(le);
   0x0000000000400535 <+15>:    mov    -0x8(%rbp),%eax
   0x0000000000400538 <+18>:    bswap  %eax
   0x000000000040053a <+20>:    mov    %eax,-0x4(%rbp)

There is just one BSWAP instruction indeed.

So, if we do care about the performance, we should use those built-in functions instead of any other method of byte reversing. Just my 2 cents.

8
votes

"I swap each bytes right?" -> yes, to convert between little and big endian, you just give the bytes the opposite order. But at first realize few things:

  • size of uint32_t is 32bits, which is 4 bytes, which is 8 HEX digits
  • mask 0xf retrieves the 4 least significant bits, to retrieve 8 bits, you need 0xff

so in case you want to swap the order of 4 bytes with that kind of masks, you could:

uint32_t res = 0;
b0 = (num & 0xff) << 24;        ; least significant to most significant
b1 = (num & 0xff00) << 8;       ; 2nd least sig. to 2nd most sig.
b2 = (num & 0xff0000) >> 8;     ; 2nd most sig. to 2nd least sig.
b3 = (num & 0xff000000) >> 24;  ; most sig. to least sig.
res = b0 | b1 | b2 | b3 ;
4
votes

You could do this:

int x = 0x12345678;

x = ( x >> 24 ) | (( x << 8) & 0x00ff0000 )| ((x >> 8) & 0x0000ff00) | ( x << 24)  ; 

printf("value = %x", x);  // x will be printed as 0x78563412
3
votes

One slightly different way of tackling this that can sometimes be useful is to have a union of the sixteen or thirty-two bit value and an array of chars. I've just been doing this when getting serial messages that come in with big endian order, yet am working on a little endian micro.

union MessageLengthUnion
{

    uint16_t asInt;
    uint8_t asChars[2];

};

Then when I get the messages in I put the first received uint8 in .asChars[1], the second in .asChars[0] then I access it as the .asInt part of the union in the rest of my program.

If you have a thirty-two bit value to store you can have the array four long.

2
votes

I am assuming you are on linux

Include "byteswap.h" & Use int32_t bswap_32(int32_t argument);

It is logical view, In actual see, /usr/include/byteswap.h

1
votes

one more suggestion :

unsigned int a = 0xABCDEF23;
a = ((a&(0x0000FFFF)) << 16) | ((a&(0xFFFF0000)) >> 16);
a = ((a&(0x00FF00FF)) << 8) | ((a&(0xFF00FF00)) >>8);
printf("%0x\n",a);
1
votes

A Simple C program to convert from little to big

#include <stdio.h>

int main() {
unsigned int little=0x1234ABCD,big=0;
unsigned char tmp=0,l;

printf(" Little endian little=%x\n",little);

for(l=0;l < 4;l++) 
{
    tmp=0;
    tmp = little | tmp;
    big = tmp | (big << 8);
    little = little >> 8;
}
printf(" Big endian big=%x\n",big);

return 0;
}
1
votes

OP's code is incorrect for the following reasons:

  • The swaps are being performed on a nibble (4-bit) boundary, instead of a byte (8-bit) boundary.
  • The shift-left << operations of the final four swaps are incorrect, they should be shift-right >> operations and their shift values would also need to be corrected.
  • The use of intermediary storage is unnecessary, and the code can therefore be rewritten to be more concise/recognizable. In doing so, some compilers will be able to better-optimize the code by recognizing the oft-used pattern.

Consider the following code, which efficiently converts an unsigned value:

// Swap endian (big to little) or (little to big)
uint32_t num = 0x12345678;
uint32_t res =
    ((num & 0x000000FF) << 24) |
    ((num & 0x0000FF00) << 8) |
    ((num & 0x00FF0000) >> 8) |
    ((num & 0xFF000000) >> 24);

printf("%0x\n", res);

The result is represented here in both binary and hex, notice how the bytes have swapped:

‭0111 1000 0101 0110 0011 0100 0001 0010‬

78563412

Optimizing

In terms of performance, leave it to the compiler to optimize your code when possible. You should avoid unnecessary data structures like arrays for simple algorithms like this, doing so will usually cause different instruction behavior such as accessing RAM instead of using CPU registers.

0
votes

You can use the lib functions. They boil down to assembly, but if you are open to alternate implementations in C, here they are (assuming int is 32-bits) :

void byte_swap16(unsigned short int *pVal16) {

//#define method_one 1
// #define method_two 1
#define method_three 1
#ifdef method_one
    unsigned char *pByte;

    pByte = (unsigned char *) pVal16;
    *pVal16 = (pByte[0] << 8) | pByte[1];
#endif

#ifdef method_two
    unsigned char *pByte0;
    unsigned char *pByte1;

    pByte0 = (unsigned char *) pVal16;
    pByte1 = pByte0 + 1;
    *pByte0 = *pByte0 ^ *pByte1;
    *pByte1 = *pByte0 ^ *pByte1;
    *pByte0 = *pByte0 ^ *pByte1;
#endif

#ifdef method_three
    unsigned char *pByte;

    pByte = (unsigned char *) pVal16;
    pByte[0] = pByte[0] ^ pByte[1];
    pByte[1] = pByte[0] ^ pByte[1];
    pByte[0] = pByte[0] ^ pByte[1];
#endif


}



void byte_swap32(unsigned int *pVal32) {

#ifdef method_one
    unsigned char *pByte;

    // 0x1234 5678 --> 0x7856 3412  
    pByte = (unsigned char *) pVal32;
    *pVal32 = ( pByte[0] << 24 ) | (pByte[1] << 16) | (pByte[2] << 8) | ( pByte[3] );
#endif

#if defined(method_two) || defined (method_three)
    unsigned char *pByte;

    pByte = (unsigned char *) pVal32;
    // move lsb to msb
    pByte[0] = pByte[0] ^ pByte[3];
    pByte[3] = pByte[0] ^ pByte[3];
    pByte[0] = pByte[0] ^ pByte[3];
    // move lsb to msb
    pByte[1] = pByte[1] ^ pByte[2];
    pByte[2] = pByte[1] ^ pByte[2];
    pByte[1] = pByte[1] ^ pByte[2];
#endif
}

And the usage is performed like so:

unsigned short int u16Val = 0x1234;
byte_swap16(&u16Val);
unsigned int u32Val = 0x12345678;
byte_swap32(&u32Val);
0
votes

Below is an other approach that was useful for me

convertLittleEndianByteArrayToBigEndianByteArray (byte littlendianByte[], byte bigEndianByte[], int ArraySize){
    int i =0;

    for(i =0;i<ArraySize;i++){
      bigEndianByte[i] = (littlendianByte[ArraySize-i-1] << 7 & 0x80) | (littlendianByte[ArraySize-i-1] << 5 & 0x40) |
                            (littlendianByte[ArraySize-i-1] << 3 & 0x20) | (littlendianByte[ArraySize-i-1] << 1 & 0x10) |
                            (littlendianByte[ArraySize-i-1] >>1 & 0x08) | (littlendianByte[ArraySize-i-1] >> 3 & 0x04) |
                            (littlendianByte[ArraySize-i-1] >>5 & 0x02) | (littlendianByte[ArraySize-i-1] >> 7 & 0x01) ;
    }
}
0
votes

Below program produce the result as needed:

#include <stdio.h>
 
unsigned int Little_To_Big_Endian(unsigned int num);
 
int main( )
{
    int num = 0x11223344 ;
    
    printf("\n Little_Endian = 0x%X\n",num);
    
    printf("\n Big_Endian    = 0x%X\n",Little_To_Big_Endian(num));
 
}
 
unsigned int Little_To_Big_Endian(unsigned int num)
{
    return (((num >> 24) & 0x000000ff) | ((num >> 8) & 0x0000ff00) | ((num << 8) & 0x00ff0000) | ((num << 24) & 0xff000000));
}

And also below function can be used:

    unsigned int Little_To_Big_Endian(unsigned int num)
    {
        return (((num & 0x000000ff) << 24) | ((num & 0x0000ff00) << 8 ) | ((num & 0x00ff0000) >> 8) | ((num & 0xff000000) >> 24 ));
    }
0
votes
#include<stdio.h>
int main(){
        int var = 0X12345678;
        var =  ((0X000000FF & var)<<24)|
               ((0X0000FF00 & var)<<8) |
               ((0X00FF0000 & var)>>8) |
               ((0XFF000000 & var)>>24);
        printf("%x",var);

}
0
votes

Here is a little function I wrote that works pretty good, its probably not portable to every single machine or as fast a single cpu instruction, but should work for most. It can handle numbers up to 32 byte (256 bit) and works for both big and little endian swaps. The nicest part about this function is you can point it into a byte array coming off or going on the wire and swap the bytes inline before converting.

#include <stdio.h>
#include <string.h>

void byteSwap(char**,int);

int main() {

    //32 bit
    int test32 = 0x12345678;
    printf("\n BigEndian = 0x%X\n",test32);

    char* pTest32 = (char*) &test32;

    //convert to little endian
    byteSwap((char**)&pTest32, 4);
    printf("\n LittleEndian = 0x%X\n", test32);

    //64 bit
    long int test64 = 0x1234567891234567LL;
    printf("\n BigEndian = 0x%lx\n",test64);

    char* pTest64 = (char*) &test64;

    //convert to little endian
    byteSwap((char**)&pTest64,8);
    printf("\n LittleEndian = 0x%lx\n",test64);

    //back to big endian
    byteSwap((char**)&pTest64,8);
    printf("\n BigEndian = 0x%lx\n",test64);

    return 0;
}


void byteSwap(char** src,int size) {
    int x = 0;
    char b[32];
    while(size-- >= 0) { b[x++] = (*src)[size]; };
    memcpy(*src,&b,x);
}

output:

$gcc -o main *.c -lm
$main

 BigEndian = 0x12345678

 LittleEndian = 0x78563412

 BigEndian = 0x1234567891234567

 LittleEndian = 0x6745239178563412

 BigEndian = 0x1234567891234567