Sagar
Sagar

Reputation: 9503

Changing endianness on 3 byte integer

I am receiving a 3-byte integer, which I'm storing in an array. For now, assume the array is unsigned char myarray[3]

Normally, I would convert this into a standard int using:

int mynum = ((myarray[2] << 16) | (myarray[1] << 8) | (myarray[0]));

However, before I can do this, I need to convert the data from network to host byte ordering.

So, I change the above to (it comes in 0-1-2, but it's n to h, so 0-2-1 is what I want):

int mynum = ((myarray[1] << 16) | (myarray[2] << 8) | (myarray[0]));

However, this does not seem to work. For the life of me can't figure this out. I've looked at it so much that at this point I think I'm fried and just confusing myself. Is what I am doing correct? Is there a better way? Would the following work?

int mynum = ((myarray[2] << 16) | (myarray[1] << 8) | (myarray[0]));
int correctnum = ntohl(mynum);

Upvotes: 3

Views: 2617

Answers (5)

phuclv
phuclv

Reputation: 41794

Why don't just receive into the top 3 bytes of a 4-byte buffer? After that you could use ntohl which is just a byte swap instruction in most architectures. In some optimization levels it'll be faster than simple bitshifts and or

union
{
    int32_t val;
    unsigned char myarray[4];
} data;

memcpy(&data, buffer, 3);
data.myarray[3] = 0;
data.val = ntohl(data.val);

or in case you have copied it to the bottom 3 bytes then another shift is enough

memcpy(&data.myarray[1], buffer, 3);
data.myarray[0] = 0;
data.val = ntohl(data.val) >> 8; // or data.val = ntohl(data.val << 8);

Upvotes: 1

Yuri
Yuri

Reputation: 351

if you receive the value in network ordering (that is big endian) you have this situation:

myarray[0] = most significant byte
myarray[1] = middle byte
myarray[2] = least significant byte

so this should work:

int result = (((int) myarray[0]) << 16) | (((int) myarray[1]) << 8) | ((int) myarray[2]);

Upvotes: 3

minimax
minimax

Reputation: 21

Beside the ways of using strucures / unions with byte-size members you have two other ways

  1. Using ntoh / hton and masking out the high byte of the 4-byte integer before or after the conversion with an bitwise and.

  2. Doing the bitshift operations contained in other answers

At any rate you should not rely on side effects and shift data beyond the size of data type. Shift by 16 is beyond the size of unsigned char and will cause problems depending on compiler, flags, platform endianess and byte order. So always do the proper cast before bitwise to make it work on any compiler / platform:

int result = (((int) myarray[0]) << 16) | (((int) myarray[1]) << 8) | ((int) myarray[2]);

Upvotes: 1

Travis Griggs
Travis Griggs

Reputation: 22252

Here's an alternate idea. Why not just make it structured and make it explicit what you're doing. Some of the confusion you're having may be rooted in the "I'm storing in an array" premise. If instead, you defined

typedef struct {
    u8 highByte;
    u8 midByte;
    u8 lowByte;
} ThreeByteInt;

To turn it into an int, you just do

u32 ThreeByteTo32(ThreeByteInt *bytes) {
    return (bytes->highByte << 16) + (bytes->midByte << 8) + (bytes->lowByte);
}

Upvotes: 4

John Zwinck
John Zwinck

Reputation: 249153

unsigned char myarray[3] = { 1, 2, 3 };
# if LITTLE_ENDIAN // you figure out a way to express this on your platform
    int mynum = (myarray[0] <<  0) | (myarray[1] <<  8) | (myarray[2] << 16);
# else
    int mynum = (myarray[0] << 16) | (myarray[1] <<  8) | (myarray[2] <<  0);
# endif
printf("%x\n", mynum);

That prints 30201 which I think is what you want. The key is to realize that you have to shift the bytes differently per-platform: you can't easily use ntohl() because you don't know where to put the extra zero byte.

Upvotes: 0

Related Questions