1. 程式人生 > >Count the consecutive zero bits (trailing) on the right with multiply and lookup

Count the consecutive zero bits (trailing) on the right with multiply and lookup

驗證 %d vol tip inf mat cit number failed

我在網上看到了一點神奇的代碼,用來計算一個數字末尾連續零的個數。

剛好我在優化一個I2C讀寫函數(只寫入I2C特定bit),覺得這個很有用。經過嘗試,確實沒問題。

下面我隆重介紹一下:

Count the consecutive zero bits (trailing) on the right with multiply and lookup

unsigned int v;  // find the number of trailing zeros in 32-bit v 
int r;           // result goes here
static const int MultiplyDeBruijnBitPosition[32] = 
{
  0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
  31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
r = MultiplyDeBruijnBitPosition[((uint32_t)((v & -v) * 0x077CB531U)) >> 27];

Converting bit vectors to indices of set bits is an example use for this. It requires one more operation than the earlier one involving modulus division, but the multiply may be faster. The expression (v & -v) extracts the least significant 1 bit from v. The constant 0x077CB531UL is a de Bruijn sequence, which produces a unique pattern of bits into the high 5 bits for each possible bit position that it is multiplied against. When there are no bits set, it returns 0. More information can be found by reading the paper Using de Bruijn Sequences to Index 1 in a Computer Word by Charles E. Leiserson, Harald Prokof, and Keith H. Randall.

On October 8, 2005 Andrew Shapira suggested I add this. Dustin Spicuzza asked me on April 14, 2009 to cast the result of the multiply to a 32-bit type so it would work when compiled with 64-bit ints.

以上內容轉自http://graphics.stanford.edu/~seander/bithacks.html

++++++++++++++++++++++++++++++++我是分割線++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

然後根據此方案,我們來做一個8bit的末尾連續0的計算。

主要用途是I2C讀寫時,mask 的計算。如果要讀寫某Byte的某幾個bit,可以只傳入寄存器地址和Mask位就可以了。

創建文件test.c

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 
 4 void main(int argc, **argv)
 5 {
 6     int r;
 7     unsigned char v;  
 8     static const unsigned char MultiplyDeBruijnBitPosition[8] = {0, 1, 6, 2, 7, 5, 4, 3};
 9 
10     v = (unsigned char)stroul(argv[1],NULL, 0);
11     r = MultiplyDeBruijnBitPosition[((unsigned char)((v & -v) * 0x1DU)) >> 5];
12 
13     printf("The calculated = %d\n", r);
14 }

在Linux下調試驗證:

gcc -o test test.c

./test xxx

應用實例:

mask的計算方法

1 #define BIT(nr) (1<<(nr))
2 #define _PM_MASK(BITS, POS) 3     ((unsigned char)(((1 << (BITS)) - 1) << (POS)))
4 #define PM_MASK(LEFT, RIGHT) 5     _PM_MASK((LEFT) - (RIGHT) + 1, RIGHT)

下面是帶mask的I2C寫操作。

static status_t i2c_write_mask(uint8_t reg, uint8_t mask, uint8_t data)
{
    status_t status;
    uint8_t shift = 0;
    uint8_t tmp;

    status = i2c_read_byte(reg, &tmp);
    if (status != kStatus_Success) {
        PRINTF("Failed: status=%ld, reg=%d\n", status, reg);
        goto out;
    }

    tmp &= ~mask;
    /* 0x1D is calculated from the de Bruun sequence */
    shift = MultiplyDeBruijnBitPosition[((uint8_t)((mask & -mask) * 0x1DU)) >> 5];
    data <<= shift;
    tmp |= data & mask;

    status = i2c_write_byte(reg, tmp);
    if (status != kStatus_Success) {
        PRINTF("Failed: reg=%02X, status=%ld\n", reg, status);
    }

out:
    return status;
}

本文參考了:http://www.cnblogs.com/shangdawei/p/3967505.html,非常感謝此作者的工作,讓我很快明白了De Bruijin序列的計算方法。

Count the consecutive zero bits (trailing) on the right with multiply and lookup