有很多关于如何找到给定值2的下一个幂的信息(参见参考文献),但我找不到任何以前的2的幂。
到目前为止,我找到的唯一方法是保持一张2到64的所有2的幂,并进行简单的查找。
答案 0 :(得分:31)
来自Hacker's Delight,一个不错的无分支解决方案:
uint32_t flp2 (uint32_t x)
{
x = x | (x >> 1);
x = x | (x >> 2);
x = x | (x >> 4);
x = x | (x >> 8);
x = x | (x >> 16);
return x - (x >> 1);
}
这通常需要12条指令。如果你的CPU有“计数前导零”指令,你可以少花钱。
答案 1 :(得分:2)
可能是最简单的方法(对于正数):
// find next (must be greater) power, and go one back
p = 1; while (p <= n) p <<= 1; p >>= 1;
如果您想进行优化,可以通过多种方式进行修改。
答案 2 :(得分:2)
uint32_t previous_power_of_two( uint32_t x ) {
if (x == 0) {
return 0;
}
// x--; Uncomment this, if you want a strictly less than 'x' result.
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return x - (x >> 1);
}
感谢您的回复。我会尝试总结它们并解释一下。 这个算法的作用是在第一个“1”位之后将所有位更改为“1”,因为这些位是唯一能使我们的“x”大于其先前2次幂的位。 在确定它们是'1'之后,它只是移除它们,使第一个'一个'位保持不变。取而代之的是我们之前的两个力量。
答案 3 :(得分:1)
这是后人(红宝石)的一个班轮:
2**Math.log(input, 2).floor(0)
答案 4 :(得分:0)
如果你可以获得2的更高功率,那么下一个更低的2的功率要么是下一个更高,要么是它的一半。这取决于你认为2的任何幂的“下一个更高”(以及你认为是2的下一个更低的幂)。
答案 5 :(得分:0)
怎么样?
if (tt = v >> 16)
{
r = (t = tt >> 8) ? 0x1000000 * Table256[t] : 0x10000 * Table256[tt];
}
else
{
r = (t = v >> 8) ? 0x100 * Table256[t] : Table256[v];
}
这只是来自http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup的修改方法。 这需要7次操作,替换乘法转换可能会更快。
答案 6 :(得分:0)
仅使用位操作的解决方案:
long FindLargestPowerOf2LowerThanN(long n)
{
Assert.IsTrue(n > 0);
byte digits = 0;
while (n > 0)
{
n >>= 1;
digits++;
}
return 1 << (digits - 1);
}
示例:
FindLargestPowerOf2LowerThanN(6):
Our Goal is to get 4 or 100
1) 6 is 110
2) 110 has 3 digits
3) Since we need to find the largest power of 2 lower than n we subtract 1 from digits
4) 1 << 2 is equal to 100
FindLargestPowerOf2LowerThanN(132):
Our Goal is to get 128 or 10000000
1) 6 is 10000100
2) 10000100 has 8 digits
3) Since we need to find the largest power of 2 lower than n we subtract 1 from digits
4) 1 << 7 is equal to 10000000
答案 7 :(得分:0)
g++
编译器提供了一个内置函数 __builtin_clz
来计算前导零:
所以我们可以这样做:
int previousPowerOfTwo(unsigned int x) {
return 1 << (sizeof(x)*8 - 1) - __builtin_clz(x);
}
int main () {
std::cout << previousPowerOfTwo(7) << std::endl;
std::cout << previousPowerOfTwo(31) << std::endl;
std::cout << previousPowerOfTwo(33) << std::endl;
std::cout << previousPowerOfTwo(8) << std::endl;
std::cout << previousPowerOfTwo(91) << std::endl;
return 0;
}
结果:
4
16
32
8
64
但请注意,对于 x == 0
,__builtin_clz
返回是未定义的。
答案 8 :(得分:0)
我在这里写下我的答案,以防将来需要参考。
对于 C 语言,这就是我认为是之前 2 次幂函数的“终极”解决方案。以下代码:
针对 C 语言(不是 C++),
如果编译器支持,则使用编译器内置程序生成高效代码 (CLZ or BSR instruction),
是可移植的(标准 C 语言,无汇编),内置程序除外,并且
解决编译器内置函数的未定义行为(当 x 为 0 时)。
如果您使用 C++ 编写,则可以适当地调整代码。请注意,C++20 引入了 std::bit_floor,它的作用完全相同。
#include <limits.h>
#ifdef _MSC_VER
# if _MSC_VER >= 1400
/* _BitScanReverse is introduced in Visual C++ 2005 and requires
<intrin.h> (also introduced in Visual C++ 2005). */
#include <intrin.h>
#pragma intrinsic(_BitScanReverse)
#pragma intrinsic(_BitScanReverse64)
# define HAVE_BITSCANREVERSE 1
# endif
#endif
/* Macro indicating that the compiler supports __builtin_clz().
The name HAVE_BUILTIN_CLZ seems to be the most common, but in some
projects HAVE__BUILTIN_CLZ is used instead. */
#ifdef __has_builtin
# if __has_builtin(__builtin_clz)
# define HAVE_BUILTIN_CLZ 1
# endif
#elif defined(__GNUC__)
# if (__GNUC__ > 3)
# define HAVE_BUILTIN_CLZ 1
# elif defined(__GNUC_MINOR__)
# if (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
# define HAVE_BUILTIN_CLZ 1
# endif
# endif
#endif
/**
* Returns the largest power of two that is not greater than x. If x
* is 0, returns 0.
*/
unsigned int prev_power_of_2(unsigned int x)
{
#ifdef HAVE_BITSCANREVERSE
if (x <= 0) {
return 0;
} else {
unsigned long int index;
(void) _BitScanReverse(&index, x);
return (1U << index);
}
#elif defined(HAVE_BUILTIN_CLZ)
if (x <= 0) {
return 0;
}
return (1U << (sizeof(x) * CHAR_BIT - 1 - __builtin_clz(x)));
#else
/* Fastest known solution without compiler built-ins or integer
logarithm instructions.
From the book "Hacker's Delight".
Converted to a loop for smaller code size.
("gcc -O3" will unroll this.) */
{
unsigned int shift;
for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
x |= (x >> shift);
}
}
return (x - (x >> 1));
#endif
}
unsigned long long prev_power_of_2_long_long(unsigned long long x)
{
#if (defined(HAVE_BITSCANREVERSE) && \
ULLONG_MAX == 18446744073709551615ULL)
if (x <= 0) {
return 0;
} else {
/* assert(sizeof(__int64) == sizeof(long long)); */
unsigned long int index;
(void) _BitScanReverse64(&index, x);
return (1ULL << index);
}
#elif defined(HAVE_BUILTIN_CLZ)
if (x <= 0) {
return 0;
}
return (1ULL << (sizeof(x) * CHAR_BIT - 1 - __builtin_clzll(x)));
#else
{
unsigned int shift;
for (shift = 1; shift < sizeof(x) * CHAR_BIT; shift <<= 1) {
x |= (x >> shift);
}
}
return (x - (x >> 1));
#endif
}
答案 9 :(得分:-1)
当您在基地2工作时,只需添加或删除右侧的数字,即可从2的幂跳到下一个。
例如,数字8的前两个幂是数字4.在二进制:
01000 - &gt; 0100(我们删除尾随零以获得数字4)
因此,解决前两次幂的微积分的算法是:
previousPower:= number shr 1
previousPower = number&gt;&gt; 1
(或任何其他语法)
答案 10 :(得分:-1)
这可以在一行中完成。
int nextLowerPowerOf2 = i <= 0
? 0
: ((i & (~i + 1)) == i)
? i >> 1
: (1 << (int)Math.Log(i, 2));
结果
i power_of_2
-2 0
-1 0
0 0
1 0
2 1
3 2
4 2
5 4
6 4
7 4
8 4
9 8
这是c#中更易读的版本,&lt; = 0 guard子句分发给实用程序方法。
int nextLowerPowerOf2 = IsPowerOfTwo(i)
? i >> 1 // shift it right
: GetPowerOfTwoLessThanOrEqualTo(i);
public static int GetPowerOfTwoLessThanOrEqualTo(int x)
{
return (x <= 0 ? 0 : (1 << (int)Math.Log(x, 2)));
}
public static bool IsPowerOfTwo(int x)
{
return (((x & (~x + 1)) == x) && (x > 0));
}
答案 11 :(得分:-2)
下面的代码将找到之前的2的强大功能:
int n = 100;
n /= 2;//commenting this will gives the next power of 2
n |= n>>1;
n |= n>>2;
n |= n>>4;
n |= n>>16;
System.out.println(n+1);
答案 12 :(得分:-5)
这是我目前的解决方案,用于查找任何给定正整数n中的两个的下一个和前一个幂,还有一个小函数来确定一个数是2的幂。
此实现适用于Ruby。
class Integer
def power_of_two?
(self & (self - 1) == 0)
end
def next_power_of_two
return 1 if self <= 0
val = self
val = val - 1
val = (val >> 1) | val
val = (val >> 2) | val
val = (val >> 4) | val
val = (val >> 8) | val
val = (val >> 16) | val
val = (val >> 32) | val if self.class == Bignum
val = val + 1
end
def prev_power_of_two
return 1 if self <= 0
val = self
val = val - 1
val = (val >> 1) | val
val = (val >> 2) | val
val = (val >> 4) | val
val = (val >> 8) | val
val = (val >> 16) | val
val = (val >> 32) | val if self.class == Bignum
val = val - (val >> 1)
end
end
使用示例:
10.power_of_two? => false
16.power_of_two? => true
10.next_power_of_two => 16
10.prev_power_of_two => 8
对于之前的2的幂,找到下一个并除以2比上面的方法稍慢。
我不确定它如何与Bignums合作。