经典位操作

最近看了一篇文章<<Bit Twiddling Hacks>>感觉不错,适当的做一下笔记。

原文地址:http://graphics.stanford.edu/~seander/bithacks.html#OperationCounting


1、Compute the sign of an integer

    // we want to find the sign of v
    int v;
    // the result goes here
    int sign;
    // CHAR_BIT is the number of bits per byte (normally 8).
    // if v < 0 then -1, else 0. 
    sign = -(v < 0);
    // or, to avoid branching on CPUs with flag registers (IA32):
    sign = -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
    // or, for one less instruction (but not portable):
    sign = v >> (sizeof(int) * CHAR_BIT - 1);
    // if v < 0 then -1, else +1
    sign = +1 | (v >> (sizeof(int) * CHAR_BIT - 1));
    // if v < 0 then -1, else if v > 0 then 1, else 0
    sign = (v != 0) | -(int)((unsigned int)((int)v) >> (sizeof(int) * CHAR_BIT - 1));
    // Or, for more speed but less portability:
    sign = (v != 0) | (v >> (sizeof(int) * CHAR_BIT - 1));
    // Or, for portability, brevity, and (perhaps) speed:
    sign = (v > 0) - (v < 0);
    // if v >= 0 then 1, else 0
    sign = 1 ^ ((unsigned int)v >> (sizeof(int) * CHAR_BIT - 1));

2、Detect if two integers have opposite signs

    // input values to compare signs
    int x, y;
    // true if x and y have opposite signs
    bool f = ((x ^ y) < 0);

3、Compute the integer absolute value (abs) without branching

    // we want to find the absolute value of v
    int v;
    // the result goes here
    unsigned int r;
    // if v >= 0 then 0, else -1
    const int mask = v >> (sizeof(int) * CHAR_BIT - 1);
    // general method
    r = (v < 0) ? -(unsigned int)v : v;
    // if v >= 0 then r = (v + 0) ^ 0
    // if v < 0 then r = (v + (-1)) ^ (-1)
    r = (v + mask) ^ mask;
    // patented variation
    // if v < 0 then r = (v ^ (-1)) + 1
    r = (v ^ mask) - mask;

4、Compute the minimum (min) or maximum(max) of two integers without branching 

    // we want to find the minimum of x and y
    int x;
    int y;
    // the result goes here
    int r;
    // min(x, y)
    r = y ^ ((x ^ y) & -(x < y));
    // max(x, y)
    r = x ^ ((x ^ y) & -(x < y));
    // quick and dirty versions
    // if INT_MIN <= x - y <= INT_MAX
    // min(x, y)
    r = y + ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)));
    // max(x, y)
    r = x - ((x - y) & ((x - y) >> (sizeof(int) * CHAR_BIT - 1)));

5、Determining if an integer is a power of 2

    // we want to see if v is a power of 2
    unsigned int v ;
    // the result goes here
    bool f;
    // include 0, but 0 is incorrect
    f = (v & (v - 1)) == 0;
    // not include 0
    f = v && !(v & (v - 1));

6、Sign entending from a constant bit-width

In C, sign extension from a constant bit-width is trival, since bit fields may be specified in structs or unions. For example, to convert from 5 bits to an full integer.

    int x;
    int r;
    struct { signed int x:5; } s;
    r = s.x = x;

template<typename T, unsigned B>
inline T signextend(const T x) {
    struct { signed int x:B; } s;
    return s.x = x;
}
int r = signextend<signed int, 5>(x);


7、 Counting bits set

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v

for (c = 0; v; v >>= 1) {
  c += v & 1;
}


8、Counting bits set, Brian Kernighan's way

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++) {
  v &= v - 1; // clear the least significant bit set
}


Counting bits set in 14, 24, or 32-bit words using 64-bit instructions

Counting bits set,  in parallel

Count bits set (rank) from the most-significant bit upto a given position

Select the bit position(from the most-significant bit) with the given count(rank)

上面这些看上去很复杂的样子,头大了,先跳过吧。


9、 Computing parity the naive way

unsigned int v;       // word value to compute the parity of
bool parity = false;  // parity will be the parity of v

while (v) {
  parity = !parity;
  v = v & (v - 1);
}

10、 Compute parity by lookup table 

static const bool ParityTable256[256] = 
{
#   define P2(n) n, n^1, n^1, n
#   define P4(n) P2(n), P2(n^1), P2(n^1), P2(n)
#   define P6(n) P4(n), P4(n^1), P4(n^1), P4(n)
    P6(0), P6(1), P6(1), P6(0)
};

unsigned char b;  // byte value to compute the parity of
bool parity = ParityTable256[b];

// OR, for 32-bit words:
unsigned int v;
v ^= v >> 16;
v ^= v >> 8;
bool parity = ParityTable256[v & 0xff];

// Variation:
unsigned char * p = (unsigned char *) &v;
parity = ParityTable256[p[0] ^ p[1] ^ p[2] ^ p[3]];

11、Compute parity of a byte using 64-bit multiply and modulus division

unsigned char b;  // byte value to compute the parity of
bool parity = 
  (((b * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1;

12、Compute parity of word with a multiply 

The following method computers the parity of the 32-bit value in only 8 operations using a multiply.

    unsigned int v; // 32-bit word
    v ^= v >> 1;
    v ^= v >> 2;
    v = (v & 0x11111111U) * 0x11111111U;
    return (v >> 28) & 1;

Also for 64-bits, 8 operators are still enough.

    unsigned long long v; // 64-bit word
    v ^= v >> 1;
    v ^= v >> 2;
    v = (v & 0x1111111111111111UL) * 0x1111111111111111UL;
    return (v >> 60) & 1;

13、 Compute parity in parallel

unsigned int v;  // word value to compute the parity of
v ^= v >> 16;
v ^= v >> 8;
v ^= v >> 4;
v &= 0xf;
return (0x6996 >> v) & 1;

14、 Swapping values with subtraction and addition

#define SWAP(a, b) ((&(a) == &(b)) || \
                    (((a) -= (b)), ((b) += (a)), ((a) = (b) - (a))))

15、Swapping values with XOR

#define SWAP(a, b) (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))

16、 Swapping individual bits with XOR

    // positions of bit sequences to swap
    unsigned int i, j;
    // number of consecutive bits in each sequence
    unsigned int n;
    // bits to swap reside in b
    unsigned int b;
    // bit-swapped result goes here
    unsigned int r;
    // XOR temporary
    unsigned int x = ((b >> i) ^ (b >> j)) & ((1U << n) - 1);

    r = b ^ ((x << i) | (x << j));
As an example of swapping ranges of bits suppose we have have b = 00101111 (expressed in binary) and we want to swap the n = 3 consecutive bits starting at i = 1 (the second bit from the right) with the 3 consecutive bits starting at j = 5; the result would be r = 11100011 (binary).

This method of swapping is similar to the general purpose XOR swap trick, but intended for operating on individual bits.  The variable x stores the result of XORing the pairs of bit values we want to swap, and then the bits are set to the result of themselves XORed with x.  Of course, the result is undefined if the sequences overlap.


17、Reverse bits the obvious way

    unsigned int v;
    // get the last bit of v
    unsigned int r = v;
    int s = sizeof(v) * CHAR_BIT - 1;
    for(v >>= 1; v; v >>= 1) {
        r <<= 1;
        r |= (v&1);
        s--;
    }
    // the lead zero
    r <<= s;

    unsigned int v;
    // the inital val of f is 0
    unsigned int r = 0;
    // the total bits of v
    int s = sizeof(v) * CHAR_BIT;
    for( ; v; v >>= 1) {
        r <<= 1;
        r |= (v & 1);
        s--;
    }
    // the lead zero
    r <<= s;

18、 Reverse bits in word by lookup table

直接贴作者的代码了...

static const unsigned char BitReverseTable256[256] = 
{
#   define R2(n)     n,     n + 2*64,     n + 1*64,     n + 3*64
#   define R4(n) R2(n), R2(n + 2*16), R2(n + 1*16), R2(n + 3*16)
#   define R6(n) R4(n), R4(n + 2*4 ), R4(n + 1*4 ), R4(n + 3*4 )
    R6(0), R6(2), R6(1), R6(3)
};

unsigned int v; // reverse 32-bit value, 8 bits at time
unsigned int c; // c will get v reversed

// Option 1:
c = (BitReverseTable256[v & 0xff] << 24) | 
    (BitReverseTable256[(v >> 8) & 0xff] << 16) | 
    (BitReverseTable256[(v >> 16) & 0xff] << 8) |
    (BitReverseTable256[(v >> 24) & 0xff]);

// Option 2:
unsigned char * p = (unsigned char *) &v;
unsigned char * q = (unsigned char *) &c;
q[3] = BitReverseTable256[p[0]]; 
q[2] = BitReverseTable256[p[1]]; 
q[1] = BitReverseTable256[p[2]]; 
q[0] = BitReverseTable256[p[3]];

19、Compute modulus division by 1 << s without a division operator

const unsigned int n;          // numerator
const unsigned int s;
const unsigned int d = 1U << s; // So d will be one of: 1, 2, 4, 8, 16, 32, ...
unsigned int m;                // m will be n % d
m = n & (d - 1); 

20、 Compute modulus division by (1 << s) - 1 without a division operator

    unsigned int n;                     // numerator
    const unsigned int s;               // s > 0
    const unsigned int d = (1 << s) - 1;// so d is either 1, 3, 7, 15, 31, ....
    unsigned int m;                     // n % d goes here
    for(m = n; n > d; n = m) {
        for(m = 0; n; n >>= s) {
            m += n & d;
        }
    }
    // Now m is a value from 0 to d, but since with modulus division
    // we want m to be 0 when it is d
    m = m == d ? 0 : m;

21、Find the log base 2 of an integer with the MSB N set in O(N) operations (the obvious way)

    // 32-bit word to find the log base 2 of
    unsigned int v;
    // r will be lg(v)
    unsigned int r = 0;
    while(v >>= 1) {
        ++r;
    }

22、 Count the consecutive zero bits (trailing) on the right linearly

    // input to count trailing zero bits
    unsigned int v;
    // output: c will count v's trailing zero bits
    // so if v is 1101000(base 2), then c will be 3
    int c;
    if(v) {
        v = (v ^ (v - 1)) >> 1;
        for(c = 0; v; v >>= 1, ++c);
        /*
        for(c = 0; v; ++c) {
            v >>= 1;
        }
        */
    } else {
        c = sizeof(v) * CHAR_BIT;
    }

23、 Count the consecutive zero bits (trailing) on the right by binary search

    // 32-bit word input to count zero bits on right
    unsigned int v;
    // c will be the number of zero bits on the right
    // so if v is 1101000(base 2), then c will be 3
    unsigned int c;

    // NOTE : if 0 == v, then c = 31
    if(v & 0x1) {
        // special case for odd v (assumed to happen half of the time)
        c = 0;
    } else {
        c = 1;
        if((v & 0xffff) == 0) {
            v >>= 16;
            c += 16;
        }
        if((v & 0xff) == 0) {
            v >>= 8;
            c += 8;
        }
        if((v & 0xf) == 0) {
            v >>= 4;
            c += 4;
        }
        if((v & 0x3) == 0) {
            v >>= 2;
            c += 2;
        }
        c -= v & 0x1;
    }

24、 Interleave bits the obvious way

unsigned short x;   // Interleave bits of x and y, so that all of the
unsigned short y;   // bits of x are in the even positions and y in the odd;
unsigned int z = 0; // z gets the resulting Morton Number.

for (int i = 0; i < sizeof(x) * CHAR_BIT; i++) // unroll for more speed...
{
  z |= (x & 1U << i) << i | (y & 1U << i) << (i + 1);
}
Interleaved bits (aka Morton numbers) are useful for linearizing 2D integer coordinates, so x and y are combined into a single number that can be compared easily and has the property that a number is usually close to another if their x and y values are close.

25、 Compute the lexicographically next bit permutation

Suppose we have a pattern of N bits set to 1 in an integer and we want the next permutation of N 1 bits in a lexicographical sense. For example, if N is 3 and the bit pattern is 00010011, the next patterns would be 00010101, 00010110, 00011001,00011010, 00011100, 00100011, and so forth. The following is a fast way to compute the next permutation.

unsigned int v; // current permutation of bits 
unsigned int w; // next permutation of bits

unsigned int t = v | (v - 1); // t gets v's least significant 0 bits set to 1
// Next set to 1 the most significant bit to change, 
// set to 0 the least significant ones, and add the necessary 1 bits.
w = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(v) + 1));  
The __builtin_ctz(v) GNU C compiler intrinsic for x86 CPUs returns the number of trailing zeros. If you are using Microsoft compilers for x86, the intrinsic is _BitScanForward. These both emit a bsf instruction, but equivalents may be available for other architectures. If not, then consider using one of the methods for counting the consecutive zero bits mentioned earlier.


更多内容参照原文,我只是把我能理解的代码摘了下来,更多神奇的代码请看原文。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值