how to replace given nibbles with another set of nibbles in an integer

2.6k views Asked by At

Suppose you have an integer a = 0x12345678 & a short b = 0xabcd

What i wanna do is replace the given nibbles in integer a with nibbles from short b

Eg: Replace 0,2,5,7th nibbles in a = 0x12345678 (where 8 = 0th nibble, 7=1st nibble, 6=2nd nibble and so on...) with nibbles from b = 0xabcd (where d = 0th nibble, c=1st nibble, b=2nd nibble & so on...)

My approach is -

  1. Clear the bits we're going to replace from a. like a = 0x02045070
  2. Create the mask from the short b like mask = 0xa0b00c0d
  3. bitwise OR them to get the result. result = a| mask i.e result = 0xa2b45c7d hence nibbles replaced.

My problem is I don't know any efficient way to create the desired mask (like in step 2) from the given short b

If you can give me an efficient way of doing so, it would be a great help to me and I thank you for that in advance ;)

Please ask if more info needed.

EDIT:
My code to solve the problem (not good enough though)
Any improvement is highly appreciated.

int index[4] = {0,1,5,7}; // Given nibbles to be replaced in integer
int s = 0x01024300; // integer mask i.e. cleared nibbles
    int r = 0x0000abcd; // short (converted to int )
     r = ((r & 0x0000000f) << 4*(index[0]-0)) | 
         ((r & 0x000000f0) << 4*(index[1]-1)) | 
         ((r & 0x00000f00) << 4*(index[2]-2)) |
         ((r & 0x0000f000) << 4*(index[3]-3));
    s = s|r;
5

There are 5 answers

2
2501 On

Nibble has 4 bits, and according to your indexing scheme, the zeroth nibble is represented by least significant bits at positions 0-3, the first nibble is represented by least significant bits at positions 4-7, and so on.

Simply shift the values the necessary amount. This will set the nibble at position set by the variable index:

size_t index = 5;    //6th nibble is at index 5
size_t shift = 4 * index;    //6th nibble is represented by bits 20-23
unsigned long nibble = 0xC;
unsigned long result = 0x12345678;
result = result & ~( 0xFu << shift );    //clear the 6th nibble
result = result | ( nibble << shift );    //set the 6th nibble

If you want to set more than one value, put this code in a loop. The variable index should be changed to an array of values, and variable nibble could also be an array of values, or it could contain more than one nibble, in which case you extract them one by one by shifting values to the right.

2
David C. Rankin On

If I understand your goal, the fun you are having comes from the reversal of the order of your fill from the upper half to the lower half of your final number. (instead of 0, 2, 4, 6, you want 0, 2, 5, 7) It isn't any more difficult, but it does make you count where the holes are in the final number. If I understood, then you could mask with 0x0f0ff0f0 and then fill in the zeros with shifts of 16, 12, 4 and 0. For example:

#include <stdio.h>

int main (void) {

    unsigned a = 0x12345678, c = 0, mask = 0x0f0ff0f0;
    unsigned short b = 0xabcd;

    /* mask a, fill in the holes with the bits from b */
    c = (a & mask) | (((unsigned)b & 0xf000) << 16);
    c |= (((unsigned)b & 0x0f00) << 12);
    c |= (((unsigned)b & 0x00f0) << 4);
    c |= (unsigned)b & 0x000f;

    printf (" a : 0x%08x\n b : 0x%0hx\n c : 0x%08x\n", a, b, c);

    return 0;
}

Example Use/Output

$ ./bin/bit_swap_nibble
 a : 0x12345678
 b : 0xabcd
 c : 0xa2b45c7d

Let me know if I misunderstood, I'm happy to help further.

0
BeeOnRope On

A lot depends on how your flexible you are in accepting the "nibble list" index[4] in your case.

You mentioned that you can replace anywhere from 0 to 8 nibbles. If you take your nibble bits as an 8-bit bitmap, rather than as a list, you can use the bitmap as a lookup in a 256-entry table, which maps from bitmap to a (fixed) mask with 1s in the nibble positions. For example, for the nibble list {1, 3}, you'd have the bitmap 0b00001010 which would map to the mask 0x0000F0F0.

Then you can use pdep which has intrinsics on gcc, clang, icc and MSVC on x86 to expand the bits in your short to the right position. E.g., for b == 0xab you'd have _pdep_u32(b, mask) == 0x0000a0b0.

If you aren't on a platform with pdep, you can accomplish the same thing with multiplication.

1
J. Piquard On

To be able to change easy the nibbles assignment, a bit-field union structure could be used:

Step 1 - create a union allowing to have nibbles access

typedef union u_nibble {
    uint32_t dwValue;
    uint16_t wValue;
    struct sNibble {
        uint32_t nib0: 4;
        uint32_t nib1: 4;
        uint32_t nib2: 4;
        uint32_t nib3: 4;
        uint32_t nib4: 4;
        uint32_t nib5: 4;
        uint32_t nib6: 4;
        uint32_t nib7: 4;
    } uNibble;
} NIBBLE;

Step 2 - assign two NIBBLE items with your integer a and short b

NIBBLE myNibbles[2];
uint32_t a = 0x12345678;
uint16_t b = 0xabcd;

myNibbles[0].dwValue = a;
myNibbles[1].wValue = b;

Step 3 - initialize nibbles of a by nibbles of b

printf("a = %08x\n",myNibbles[0].dwValue);
myNibbles[0].uNibble.nib0 = myNibbles[1].uNibble.nib0;
myNibbles[0].uNibble.nib2 = myNibbles[1].uNibble.nib1;
myNibbles[0].uNibble.nib5 = myNibbles[1].uNibble.nib2;
myNibbles[0].uNibble.nib7 = myNibbles[1].uNibble.nib3;
printf("a = %08x\n",myNibbles[0].dwValue);

Output will be:

a = 12345678
a = a2b45c7d
0
sameerkn On

With nibble = 4 bits and unsigned int = 32 bits, a nibble inside a unsigned int can be found as follows:

x = 0x00a0b000, find 3rd nibble in x i.e locate 'b'. Note nibble index starts with 0.

Now 3rd nibble is from 12th bit to 15th bit.

3rd_nibble can be selected with n = 2^16 - 2^12. So, in n all the bits in 3rd nibble will be 1 and all the bits in other nibbles will be 0. That is, n=0x00001000

In general, suppose if you want to find a continuous sequence of 1 in binary representation in which sequence starts from Xth bit to Yth bit then formula is 2^(Y+1) - 2^X.

#include <stdio.h>

#define BUF_SIZE 33

char *int2bin(int a, char *buffer, int buf_size)
{
    int i;
    buffer[BUF_SIZE - 1] = '\0';

    buffer += (buf_size - 1);

    for(i = 31; i >= 0; i--)
    {
            *buffer-- = (a & 1) + '0';
            a >>= 1;
    }

    return buffer;
}


int main()
{
    unsigned int a = 0;
    unsigned int b = 65535;
    unsigned int b_nibble;
    unsigned int b_at_a;
    unsigned int a_nibble_clear;
    char replace_with[8];
    unsigned int ai;
    char buffer[BUF_SIZE];

    memset(replace_with, -1, sizeof(replace_with));
    replace_with[0] = 0; //replace 0th nibble of a with 0th nibble of b
    replace_with[2] = 1; //replace 2nd nibble of a with 1st nibble of b
    replace_with[5] = 2; //replace 5th nibble of a with 2nd nibble of b
    replace_with[7] = 3; //replace 7th nibble of a with 3rd nibble of b

    int2bin(a, buffer, BUF_SIZE - 1);
    printf("a               = %s, %08x\n", buffer, a);
    int2bin(b, buffer, BUF_SIZE - 1);
    printf("b               = %s, %08x\n", buffer, b);

    for(ai = 0; ai < 8; ++ai)
    {
            if(replace_with[ai] != -1)
            {
                    b_nibble = (b & (1LL << ((replace_with[ai] + 1)*4)) - (1LL << (replace_with[ai]*4))) >> (replace_with[ai]*4);
                    b_at_a = b_nibble << (ai * 4);
                    a_nibble_clear = (a & ~(a & (1LL << ((ai + 1) * 4)) - (1LL << (ai * 4))));
                    a = a_nibble_clear | b_at_a;
            }
    }

    int2bin(a, buffer, BUF_SIZE - 1);
    printf("a               = %s, %08x\n", buffer, a);


    return 0;
}


Output:
a               = 00000000000000000000000000000000, 00000000
b               = 00000000000000001111111111111111, 0000ffff
a               = 11110000111100000000111100001111, f0f00f0f