C++ 计算每4次方的倍数

C++ 计算每4次方的倍数,c++,bit-manipulation,bitwise-operators,C++,Bit Manipulation,Bitwise Operators,给定一个数,n,我需要有效地找出这个数是给定数的所有4次幂的倍数 例如: 16是4和16的倍数,因此结果为2 64是4、16和64的倍数,因此结果为3 256是4、16、64和256的倍数,因此结果为4 14不是4的任何幂的倍数,因此结果为0 35不是4的任何幂的倍数,因此结果为0 按位操作是首选,这是在一个非常紧密的循环中,因此它位于需要高效的瓶颈内。目前我的代码是显而易见的答案,但我必须相信,有一种更为数学化的方法可以用更少的步骤计算出结果: power = 4; while (powe

给定一个数,n,我需要有效地找出这个数是给定数的所有4次幂的倍数

例如:

  • 16是4和16的倍数,因此结果为2
  • 64是4、16和64的倍数,因此结果为3
  • 256是4、16、64和256的倍数,因此结果为4
  • 14不是4的任何幂的倍数,因此结果为0
  • 35不是4的任何幂的倍数,因此结果为0
按位操作是首选,这是在一个非常紧密的循环中,因此它位于需要高效的瓶颈内。目前我的代码是显而易见的答案,但我必须相信,有一种更为数学化的方法可以用更少的步骤计算出结果:

power = 4;
while (power < n) {
    result += !(n & (power - 1));
    power *= 4;
}
power=4;
while(功率
数学方法是一直除以
4
,直到结果不再可除以
4

如果您真的想使用逐位操作,可以使用技术来计算尾随零位的数量(即值可被
2
整除的次数)。这些可以调整为对尾随位进行计数(即,可被4而不是2的幂整除)

请注意,您将需要使用
无符号
值,以避免出现某些未定义或未指定的行为


我不同意你的说法,即按位运算将有助于更有效的解决方案。在没有测试的情况下,它不是给定的,特别是在现代编译器中。

可以使用对数。在谷歌上快速搜索“FastLog2C++”可以找到一长串的想法。然后你的答案是log2(x)/2,如果你只想得到4的精确幂的答案,你必须找到一些方法来确保你的结果是一个整数

如果您正在为x86处理器编程,则可以使用BitScanForward和BitScanReverse查找设置的位,并使用它计算log2。以下代码在VisualStudio中工作,对于GCC或其他代码,还有其他方法进行内联汇编

uint32_t exact_power_of_4_scan(uint32_t num)
{
  unsigned long reverse;
  unsigned long forward;

  if (!_BitScanReverse(&reverse, num)) return 0;
  _BitScanForward(&forward, num);

  if (reverse != forward) return 0; // makes sure only a single bit is set
  if (reverse & 0x1) return 0; // only want every other power of 2
  return reverse / 2;
}
如果您需要一个可移植的解决方案,表查找可能是一种方法,但更为复杂

uint8_t not_single_bit[256] = {
  1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};

uint8_t log2_table[256] = {
  0, 0, 1, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0,
  4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

uint32_t exact_power_of_2(uint32_t num)
{
  auto a = not_single_bit[num & 0xff];
  auto b = not_single_bit[(num >> 8) & 0xff];
  auto c = not_single_bit[(num >> 16) & 0xff];
  auto d = not_single_bit[(num >> 24) & 0xff];

  if (a + b + c + d != 3) {
    return 0;
  }

  if (!a) {
    return log2_table[num & 0xff];
  }
  if (!b) {
    return log2_table[(num >> 8) & 0xff] + 8;
  }
  if (!c) {
    return log2_table[(num >> 16) & 0xff] + 16;
  }

  return log2_table[(num >> 24) & 0xff] + 24;
}

uint32_t exact_power_of_4(uint32_t num)
{
  auto ret = exact_power_of_2(num);

  if (ret & 0x1) return 0;
  return ret / 2;
}

两者都是线性算法。第一个可能会击败几乎任何
num
值的循环,但我还没有测试它。第二种可能只适用于较大的
num
s

不错的想法,它有点疯狂,但它确实摆脱了分支循环,并且对于n的高值可能更有效。将代码添加到您的答案中,我将接受它:int temp=BSR(n);值=!(n&((11);BSR=位扫描反转