计算一个整数有的二进制表示中多少个 1

很早以前记着有这么一个技巧,计算一个整数的二进制表示多少个 1 。

template <typename T>
int count(T a) {
  int ret = 0;
  for (T x = a; x; x &= ~(x - 1) ^ x) {
    ret++;
  }
  return ret;
}

例如输出结果

 0x0 -> 0
 0x1 -> 1
 0x2 -> 1
 0x3 -> 2
 0x4 -> 1
 0x5 -> 2
 0x6 -> 2
 0x7 -> 3
 0x8 -> 1
 0x9 -> 2
 0xa -> 2
 0xb -> 3
 0xc -> 2
 0xd -> 3
 0xe -> 3
 0xf -> 4

这个是怎么工作的呢?

如果 a 是零,程序返回零。这个很容易看得出来。 如果不是零,那么假设 x 可以表示成为 b1 b2 b3 b4 1 0 0 0 ... 结尾有 N 个 0 ,一个 1b1 ... b4 有可能是 0 或者 1

那么 x-1 可以表示成为 b1 b2 b3 b4 0 1 1 1 ...

(x-1)^x 可以表示成为 0000 11111... 因为 b1 ^ b1 = 0 无论 b1 是 0 or 1

这样的我们得到一个 mask , x&= ~mask 就可以消除掉 x 最低位的一个 1 。于是不停地重复,需要重复几次,就说明有多少个 1 。于是我们的返回值就是简单计数器,计算循环了多少次。

这个严格上来说,效率比较高。因为循环次数是 1 的个数。

这个技巧属于完全无用的技巧,如果从可读性的角度来说,下面的代码的可读性更好。

template <typename T>
int count2(T a) {
  int ret = 0;
  T pin = 1;
  for (size_t i = 0; i < sizeof(T); ++i, pin<<=1){
      if(pin&a) ret++;
  }
  return ret;
}

对于喜欢装 13 的,抗击打能力比较强的,不怕挨揍的,的那些人,可以考虑在生产代码里面采用第一种姿势。后果自负。