Recently, I saw some interview questions using bit twiddling techniques. I haven’t known these tricks/techniques before, so I searched in the internet and collected a few basic knowledge to start with. The examples shown below accompanied by full C code are excerpted from “the bit twiddler”(http://bits.stephan-brumme.com/basics.html). The information on the website is very helpful, especially for beginners like me.
1.Bit Mask of lowest bit not set.
01: unsigned int lowestBitNotSet(unsigned int x)
02: {
03: return ~x & (x + 1);
04: }
05:
06: unsigned int lowestBitNotSetSimple(unsigned int x)
07: {
08: // to be consistent with lowestBitNotSet
09: if (x == ~0)
10: return 0;
11:
12: // shift right until finding a zero
13: unsigned int result = 1;
14: while ((x & result) != 0)
15: result <<= 1;
16:
17: return result;
18: }
2. Basic Operations
01: int setBit(int x, unsigned char position)
02: {
03: int mask = 1 << position;
04: return x | mask;
05: }
06:
07: int clearBit(int x, unsigned char position)
08: {
09: int mask = 1 << position;
10: return x & ~mask;
11: }
12:
13: int modifyBit(int x, unsigned char position, bool newState)
14: {
15: int mask = 1 << position;
16: int state = int(newState); // relies on true = 1 and false = 0
17: return (x & ~mask) | (-state & mask);
18: }
19:
20: int flipBit(int x, unsigned char position)
21: {
22: int mask = 1 << position;
23: return x ^ mask;
24: }
25:
26: bool isBitSet(int x, unsigned char position)
27: {
28: x >>= position;
29: return (x & 1) != 0;
30: }
3. Compute the integer absolute value (abs) without branching
int v; // we want to find the absolute value of v
unsigned int r; // the result goes here
int const mask = v >> sizeof(int) * CHAR_BIT – 1; //mask get the sign of v
r = (v + mask) ^ mask;
Patented variation:
r = (v ^ mask) – mask;
4. Compute the minimum (min) or maximum (max) of two integers without branching
a)
int x; // we want to find the minimum of x and y
int y;
int r; // the result goes here
r = y ^ ((x ^ y) & -(x < y)); // min(x, y)
On some rare machines where branching is very expensive and no condition move instructions exist, the above expression might be faster than the obvious approach, r = (x < y) ? x : y, even though it involves two more instructions. (Typically, the obvious approach is best, though.)
It works because if x < y, then -(x < y) will be all ones, so r = y ^ (x ^ y) & ~0 = y ^ x ^ y = x. Otherwise, if x >= y, then -(x < y) will be all zeros, so r = y ^ ((x ^ y) & 0) = y. On some machines, evaluating (x < y) as 0 or 1 requires a branch instruction, so there may be no advantage.
b) another version by sign-mask, easier to understand, but not portable
Following trick only works for a reduced integer range of effectively one bit less,
INT_MIN <= a – b <= INT_MAX:
If a is greater b, a – 0 is returned, otherwise a – (a – b) == +b
int max(int a, int b) {
int diff = a – b;
int dsgn = diff >> 31;
return a – (diff & dsgn);
}
Since I have gained enough basic knowledge now, I would like to proceed with one question as a practice.
Generate power set of given N distinct elements. For example, given {1,2,3} , we have following power set{}, {1}, {2}, {3},{1,2}, {1,3}, {2,3}, {1,2,3}.
#include<iostream>
#include<vector>
void printPowerSet(std::vector<int> set)
{
int n=set.size();
std::cout<<"We have following sets contained in the power set:"<<std::endl;
for (int i=0;i<(1<<n);i++)
{
std::cout<<"{ ";
for (int j=0;j<n;j++)
{
if((~i&(1<<j))==0)
std::cout<<set[j]<<" ";
}
std::cout<<"}"<<std::endl;
}
}
Related:
1.Bitwise Operators
The bitwise shift operators are:
Right shift (>>)
Left shift (<<)
These binary operators have left-to-right associativity.
The results are undefined if the right operand of a shift expression is negative or if the right operand is greater than or equal to the number of bits in the (promoted) left operand.
The left shift operator causes the bit pattern in the first operand to be shifted left the number of bits specified by the second operand. Bits vacated by the shift operation are zero-filled. This is a logical shift, as opposed to a shift-and-rotate operation.
The right shift operator causes the bit pattern in the first operand to be shifted right the number of bits specified by the second operand. Bits vacated by the shift operation are zero-filled for unsigned quantities. For signed quantities, the sign bit is propagated into the vacated bit positions. The shift is a logical shift if the left operand is an unsigned quantity; otherwise, it is an arithmetic shift.
2. Print in the hexiadecimal format
int x=16;
using namespace std;
cout<<showbase;
cout<<hex<<x<<endl;
Reference:
1.http://bits.stephan-brumme.com/basics.html
2.http://www.cprogramming.com/tutorial/bitwise_operators.html
3.http://msdn.microsoft.com/en-us/library/336xbhcz(VS.80).aspx
4.On C++ operators: http://en.wikipedia.org/wiki/Operators_in_C_and_C%2B%2B
5.Bit Twiddling Hacks, http://graphics.stanford.edu/~seander/bithacks.html#OperationCounting
6.Future Exploration can start from:
http://chessprogramming.wikispaces.com/Bit-Twiddling