HorusH
HorusH

Reputation: 231

copy n bits at given position from x to y in C

I am learning C and I am still a very beginner.

My problem is the following. I have one unisgned int x and one unsigned int y. I would like to copy n bits from position p from x to the same position on y. I found some similar problems but not in C and most of the time the problem is slightly different if one takes the most right or left bits. I would also like to find a solution not depending on integer representation on the machine.

Here is what I did

unsigned fix_bits(unsigned x, unsigned y, int n, int p)
{
    unsigned u1,u2,u3,u4,x1,y1,yf;
    u1 = ~0; /*vector of 1*/
    u2 = (u1>>n); /*0 from 0 to n-1 and 1s*/
    u3 = ~(u2);/*1 from 0 to n-1 and 0s*/
    u4 = u3>>p;/*0 from 0 to p-1, n 1 from p to p+n+1 and 0s*/
    x1 = (x & u4);/*only keep n bits of x from position p*/
    y1 = (y | u4);/*set y bit  from p to (p+n+1) to 1, rest remains unchanged (0 | bit = bit)*/
    yf = (x1 | y1);
    return yf;
}

But it does not work:

The result of placing 2 bits at position 3 of 28 to 32 is 402653216

Does someone knows what am I doing wrong ?

Thank you very much

Upvotes: 2

Views: 3593

Answers (5)

Ashutosh Tiwari
Ashutosh Tiwari

Reputation: 103

unsigned char
copy_Nbits(unsigned char num_S, unsigned char num_D, char start_off, char end_off)
{
    unsigned char u1 = 0;
    unsigned char u2 = 0;

    u1 = ~u1;
    u1 = (u1 >> ((8 * sizeof(num_S)) - 1 - end_off + start_off));
    u1 = (u1 << start_off);
    u2 = u1;
    u2 &= num_S;

    u1 = ~u1;
    u1 &= num_D;

    return (u1 | u2);
}

Upvotes: 0

ryyker
ryyker

Reputation: 23208

Regarding the problem: copy n bits from position p from x to the same position on y

Results with your code just as it is in the OP :

unsigned int x = 0xffffffff;
unsigned int y = 0x00000000;
unsigned int z = 0x00000000;
z = fix_bits(x, y, 5, 5);    

enter image description here

It looks like you are operating from the wrong end of the target number. Change your logic to work from the right (LSB), instead of the left (MSB).

Try this:

unsigned fix_bits(unsigned x, unsigned y, int n, int p)
{
    unsigned a, b, c, d, e;
    int mask;
    //Get mask
    mask = ((1<<(n))-1)<<(p-n); //[edit] corrected, was ...<<p, is ...<<(p-n)
    //apply mask to destination, 
    //XOR that with repositioned, BITwise NOTed source and apply mask
    /*so you can do these steps:
    a = mask|y;
    b = ~x;
    c = b<<p;
    d = c&mask;
    e = d^a;

    return e;*/
    //or do this one:  
    return ((mask&(~x<<p))^(mask|y)); //same thing
}  

For the shown inputs, the example output is below:

unsigned int x = 0xffffffff;
unsigned int y = 0xf0000000;
unsigned int z = 0x00000000;

z = fix_bits(x, y, 3, 20);

enter image description here

Results after correction to mask (was <<p, is <<(p-n)):

enter image description here

Upvotes: 2

akydd
akydd

Reputation: 43

I needed a similar function for building an NES emulator. Using the solution by @ryyker, I created a slightly more general function for uint16_t. It could probably be optimized. It should also meet the original poster's requirements when source_pos = dest_pos.

Perhaps someone brought here while looking to solve the general case will find this helpful.

/*                                                                                                                                                                  
 * This was fun to figure out.  Copy num bits at source_pos from source
 * into dest at dest_pos.  Positions start at 0, the LSB.
 */
uint16_t set_bits(uint16_t source, uint16_t dest, int num, int source_pos, int dest_pos)                                                                            
{
    unsigned long mask = ((1UL<<(num))-1UL)<<(source_pos);

    if (dest_pos >= source_pos) {
            return (dest & (~(mask << dest_pos))) | ((source & mask) << dest_pos);
    }

    return (dest & (~(mask >> (source_pos - dest_pos)))) | ((source & mask) >> (source_pos - dest_pos));

}

Upvotes: 0

HorusH
HorusH

Reputation: 231

So my final working solution is just

unsigned fix_bits(unsigned x, unsigned y, int n, int p)

{

    unsigned u1,u2,u3,u4,x1,y1,yf;
    u1 = ~0; /*vector of 1*/
    u2 = (u1<<n); /*0 from 0 to n-1 and 1s*/
    u3 = ~(u2);/*1 from 0 to n-1 and 0s*/
    u4 = u3<<p;/*0 from 0 to p-1, n 1 from p to p+n+1 and 0s*/
    x1 = (x & u4);/*only keep n bits of x from position p*/
    y1 = (y | u4);/*set y bit  from p to (p+n+1) to 1, rest remains unchanged (0 | bit = bit)*/
    yf = (x1 | y1);
    return yf;

}

And then

x = 28 = [0 0 ... 0 1 1 1 0]

y = 32 = [0 0 ... 1 0 0 0 0]

fix_bits(28,32,2,3) /placing two bits at position 3 from 28 to 32/

Outputs

z = 56 = [0 0 ... 1 1 1 0 0]

Upvotes: 0

godel9
godel9

Reputation: 7390

You're getting the mask wrong. Try:

unsigned mask = ((1 << n) - 1) << p;
return (y & ~mask) | (x & mask);

Upvotes: 2

Related Questions