如何计算一个uint64类型的二进制值的尾部有多少个0
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
正文
这实在是一个很简单的问题,用代码可以表示如下:
func CountBit0(x uint64) int{
cnt := 0
for i:=0; i<64; i++{
if (x>>i)&1==1{
break
}
cnt++
}
return cnt
}
但是,我们再看看golang标准库中的解决办法:
// C:\Go\src\math\bits\bits.go
const deBruijn64 = 0x03f79d71b4ca8b09
var deBruijn64tab = [64]byte{
0, 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4,
62, 47, 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5,
63, 55, 48, 27, 60, 41, 37, 16, 46, 35, 44, 21, 52, 32, 23, 11,
54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6,
}
// TrailingZeros64 returns the number of trailing zero bits in x; the result is 64 for x == 0.
func TrailingZeros64(x uint64) int {
if x == 0 {
return 64
}
// If popcount is fast, replace code below with return popcount(^x & (x - 1)).
//
// x & -x leaves only the right-most bit set in the word. Let k be the
// index of that bit. Since only a single bit is set, the value is two
// to the power of k. Multiplying by a power of two is equivalent to
// left shifting, in this case by k bits. The de Bruijn (64 bit) constant
// is such that all six bit, consecutive substrings are distinct.
// Therefore, if we have a left shifted version of this constant we can
// find by how many bits it was shifted by looking at which six bit
// substring ended up at the top of the word.
// (Knuth, volume 4, section 7.3.1)
return int(deBruijn64tab[(x&-x)*deBruijn64>>(64-6)])
}
老实说,没看懂……
我用python还原了一遍计算过程
>>> '{:b}'.format(100) #假设我们计算100这个值的二进制值后面有几个零
'1100100' # 从这里看, 答案应该是2
>>> '{:b}'.format(int(100&-100)) #一个值,与它的负值补码做 bit and,得到了尾部最后一个1开始的二进制值
'100'
'{:b}'.format(int(100&-100)*0x03f79d71b4ca8b09) # 这个 magic number,神来之笔,不懂为什么
'111111011110011101011100011011010011001010100010110000100100'
>>> '{:b}'.format( (int(100&-100)*0x03f79d71b4ca8b09) >> 58 ) # 最后再右移58个bit,得到了一个下标
'11' # 这个下标值是3
最后一步,根据下标值3去查表,得到最终结果2……
从注释来看,算法来自高德纳大师的 The Art of Computer Programming 第四卷。没文化真可怕,我很羞愧,以后一定好好看书。