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.

This exercise may look daunting at first, so let us try breaking it down and see what it is really asking for.

Imagine x is 10101010binary, y is 01010101binary, p is 510, and n is 410. What we need to do is replace the four bits starting at position five—10101010—with the last four bits of y—0101. So the value returned should be 10010110binary.

Let us break this process into steps. The first thing we need to do is replace the four bits with zeros. In order to do this, we use the AND operator as follows: 10101010binary & 11000011binary. Obviously, the bitmask 11000011 only works for this specific scenario and for other values of n and p, we would need a different mask, so let us take a look at how we will generate the bitmask in terms of n and p.

We start with ~0 (11111111binary). We then shift it n bits to the left. Doing this changes the rightmost n bits to zeros (11110000.) Next, we invert the bits (00001111) and then shift them p + 1 - n bits to the left (00111100.) We use p + 1 because the rightmost bit has position zero. Finally, we invert the bits (11000011.) Putting this all together, we get the code ~(~(~0 << n) << (p + 1 - n))) to represent our bitmask and the statement x & ~(~(~0 << n) << (p + 1 - n))) to set the necessary bits to zeros.

Next, we have to move the four bits in y to the correct location and use the OR operator to combine the two sets of bits: 10000010binary | 00010100binary. Again, let us see how we can achieve this step-by-step using the bitwise operations. First, we need to filter the last n bits of y using the bitmask 00001111. To create the mask in terms of n, we shift ~0 (11111111binary) n bits to the left (11110000) and then invert the bits (00001111.) We then use the AND operator to filter the last n bits from y (00000101). This results in the code y & ~(~0 << n). Finally, we shift the bits p + 1 - n bits to the left (00010100) and use the OR operator with the two sets of bits.

    /* setbits:  return x with rightmost n bits from y starting from p */
    unsigned setbits(unsigned x, int p, int n, unsigned y)
    {
        return (x & ~(~(~0U << n) << (p+1-n))) | ((y & ~(~0U << n)) << (p+1-n));
    }

Note: in the example we used to understand how to code our function, we assumed we were computing on a single byte (eight bits); however, in the actual function we use unsigned int, so the function works on what is most likely a 32-bit string (this does depend on the implementation.)

Note: using unsigned int means that the numbers used actually represent the bit strings (in two's complement machines, the place value of the leftmost bit is -128 for signed ints.)