Bit invert function in K&R exercise 2-7
bit-manipulation, c, kernighan-and-ritchie
Solution
`((1u<<n)-1)` is a bit mask with `n` '1' bits at the RHS. `<<p` shifts this block of ones `p` positions to the left. (you should shift with `(p-n)` instead of p if you want to count from the left).
return val ^ (((1u<<n)-1) <<p) ;
There still is a problem when p is larger than the wordsize (shifting by more than the wordsize is undefined), but that should be the responsability of the caller ;-)
For the example `101(101)10` with p=2 and n=3:
1u<<n := 1000
((1u<<n)-1) := 0111
(((1u<<n)-1) <<p) := 011100
original val := 10110110
val ^ mask := 10101010
Problem
Exercise 2-7 of The C Programming Language: Write a function `invert(x,p,n)` that returns `x` with the `n` bits that begin at position `p` inverted (i.e., 1 changed to 0 and vice versa), leaving the others unchanged. I understood the question like this: I have 182 which is `101(101)10` in binary, the part in parentheses has to be inverted without changing the rest. The return value should be `10101010` then, which is 170 in decimal. Here is my attempt: ``` #include <stdio.h> unsigned int getbits(unsigned int bitfield, int pos, int num); unsigned int invert(unsigned int bitfield, int pos, int num); int main(void) { printf("%d\n", invert(182, 4, 3)); return 0; } /* getbits: get num bits from position pos */ unsigned int getbits(unsigned int bitfield, int pos, int num) { return (bitfield >> (pos+1-n)) & ~(~0 << num); } /* invert: flip pos-num bits in bitfield */ unsigned int invert(unsigned int bitfield, int pos, int num) { unsigned int mask; unsigned int bits = getbits(bitfield,pos,num); mask = (bits << (num-1)) | ((~bits << (pos+1)) >> num); return bitfield ^ mask; } ``` It seems correct (to me), but `invert(182, 4, 3)` outputs `536870730`. `getbits()` works fine (it's straight from the book). I wrote down what happens in the expression I've assigned to `y`: ``` (00000101 << 2) | ((~00000101 << 5) >> 3) -- 000000101 is the part being flipped: 101(101)10 00010100 | ((11111010 << 5) >> 3) 00010100 | (01000000 >> 3) 00010100 | 00001000 = 00011100 10110110 (182) ^ 00011100 ---------- = 10101010 (170) ``` Should be correct, but it isn't. I found out this is where it goes wrong: `((~xpn << (p+1)) >> n)`. I don't see how. Also, I've no idea how general this code is. My first priority is to just get this case working. Help in this issue is welcome too.