Exercise 2.6 - Setting bits at a position n

Question

Write a function setbits(x,p,n,y) that returns x with the n bits that begin at position p set to the rightmost n bits of y, leaving the other bits unchanged.

/**
 *
 * Exercise 2.6 -  Write a function setbits(x,p,n,y) that returns x with the
 * n bits that begin at position p set to the rightmost n bits of y,leaving
 * the other bits unchanged.
 **/

#include <stdio.h>

unsigned setbits(unsigned x, int p, int n, unsigned y);

int main(void) { printf("%u", setbits((unsigned)12, 3, 2, (unsigned)57)); }

unsigned setbits(unsigned x, int p, int n, unsigned y) {
    return (x & ~(~(~0 << n) << (p + 1 - n))) |
           (y & (~(~0 << n)) << (p + 1 - n));
}

Explanation

The important piece of the program is this:

(x & ~(~(~0 << n) << (p+1-n))) | ( y & (~(~0<<n) << (p+1-n)));

Let’s look at some inner terms. The expression ~(~0<<n) will thus always yield right most n bits set to 1. And (p+1-n) will give a integer value for the n digits at position p we want. We do p+1 because our position is 0 indexed while count of digits n is 1 indexed. For e.g. in a number 1111 1111, the 4 digits starting the position 3 is 1111

Let’s refactor the program to understandable portions.

RIGHT_MOST_N_1S = ~(~0 << n) GET_N_DIGITS = (p + 1 -n)

The above expression becomes:

(x & ~(RIGHT_MOST_N_1S << GET_N_DIGITS)) | (( y & RIGHT_MOST_N_1S) << GET_N_DIGITS);

For a moment, let’s take the word that left shifting operation will give us expected bits. For e.g: 0000 1111 << 4 will give us 1111 0000

Reducing it further, it becomes:

(x & ~(Expected bits marked as 1) | ((Right most bits in y) << GET_N_DIGITS);

(x & (Expected bits marked as 0) | (Expected bits marked as 1 in y and rest as 0);

So in x we switch off the expected bits by and ing it to 0. And and in y, we just get expected bits we want by and ing it to 1 and we OR the two expressions and thus get the result.

Why left shift?

We left shift because we want it from a position p.

Suppose we want two bits from position 4, we we need to our bit patterns to be like this:

0001 1000
7654 3210

In the above, from position 4, we have 2 bits set 1 and rest 0.

In order to do this, we get two bits at the right most position:

~(~0 << n)
0000 0011

And then we shift it by position 4 using the expression p+1-n:

~(~0 << n) << (4+1-n)
0000 0011 << (5-2)
0000 0011 << 3
0 0011 000
0001 1000

So we determined our expected bits and we can follow the rest for the program.

Let’s get concrete and work out the program using the values:

x = 1111 1111 (= 255 )
p = 3
y = 0000 0000 (= 0)
n = 4
setbits(unsigned x,int p,int n,unsigned y);

Right most 4 bits of y = 0000 So, we our return value should be:

= 1111 0000
= 240

Let’s follow how the program determines it. We take the LHS of |

(x & ~(~(~0 << n) << (p+1-n)))

p+1-n = 3 + 1 - 4
      = 0

(~0 << n)
      = (~ 0000 0000 << 4)
      = (1111 1111 << 4)
      = (1111 0000)

~(~0 << n)
  = ~(1111 0000)
  = 0000 1111

(~(~0 << n) << (p+1-n))
  = 0000 1111 << 0
  = 0000 1111

~(~(~0 << n) << (p+1-n))
          = ~ (0000 1111)
          = 1111 0000

(x & ~(~(~0 << n) << (p+1-n)))
          =  1111 1111 & 1111 0000
          =  1111 0000

Now we take the RHS of |

(( y & ~(~0<<n)) << (p+1-n))

(p + 1 - n) = 0

~(~0 << n)  = 0000 1111

( y & ~(~0<<n))
    = 0000 0000 & 0000 1111
    = 0000 0000

(( y & ~(~0<<n)) << (p+1-n))
                = 0000 0000 << 4
                = 0000 0000

We write the entire expression:

(x & ~(~(~0 << n) << (p+1-n))) | (( y & ~(~0<<n)) << (p+1-n));
            = 1111 0000  | 0000 0000
            = 1111 0000

Converting 1111 0000 to decimal gives us 240 and that is answer.

Visualization