web-dev-qa-db-ja.com

整数が与えられた場合、ビット調整を使用して次に大きい2のべき乗を見つけるにはどうすればよいですか?

整数nがある場合、ビットごとのシフトまたはロジックによってiN要素を持つ_k > n_のような次の数値_k = 2^i_を見つけるにはどうすればよいですか。

例:_n = 123_がある場合、2で割り切れる_k = 128_ではなく、2の累乗である_124_を見つけるにはどうすればよいですか。これは単純なはずですが、それは私を避けます。

72
AndreasT

32ビット整数の場合、これは単純で簡単なルートです。

unsigned int n;

n--;
n |= n >> 1;   // Divide by 2^k for consecutive doublings of k up to 32,
n |= n >> 2;   // and then or the results.
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n++;           // The result is a number of 1 bits equal to the number
               // of bits in the original number, plus 1. That's the
               // next highest power of 2.

以下に、より具体的な例を示します。バイナリで11011101である数値221を取得します。

n--;           // 1101 1101 --> 1101 1100
n |= n >> 1;   // 1101 1100 | 0110 1110 = 1111 1110
n |= n >> 2;   // 1111 1110 | 0011 1111 = 1111 1111
n |= n >> 4;   // ...
n |= n >> 8;
n |= n >> 16;  // 1111 1111 | 1111 1111 = 1111 1111
n++;           // 1111 1111 --> 1 0000 0000

9番目の位置には1ビットがあり、これは2 ^ 8、または256、これは実際に2の次のべき乗を表します。各シフトは、数の既存の1ビットのすべてと、以前は未処理のゼロの一部とオーバーラップし、最終的に元の数のビット数に等しい1ビット数を生成します。その値に1を加えると、2の新しいべき乗が生成されます。

もう一つの例; 131を使用します。これはバイナリで10000011です。

n--;           // 1000 0011 --> 1000 0010
n |= n >> 1;   // 1000 0010 | 0100 0001 = 1100 0011
n |= n >> 2;   // 1100 0011 | 0011 0000 = 1111 0011
n |= n >> 4;   // 1111 0011 | 0000 1111 = 1111 1111
n |= n >> 8;   // ... (At this point all bits are 1, so further bitwise-or
n |= n >> 16;  //      operations produce no effect.)
n++;           // 1111 1111 --> 1 0000 0000

そして確かに、256は131から2の2のべき乗です。

整数の表現に使用されるビット数自体が2の累乗である場合、この手法を効率的かつ無期限に拡張し続けることができます(たとえば、n >> 32 64ビット整数の行)。

94
John Feminella

これには実際にアセンブリソリューションがあります(80386命令セットから)。

BSR(ビットスキャンリバース)命令を使用して、整数の最上位ビットをスキャンできます。

bsrは、ダブルワードオペランドまたは2番目のワードの最上位ビットからビットをスキャンします。ビットがすべてゼロの場合、ZFはクリアされます。そうでない場合、ZFが設定され、最初に設定されたビットのビットインデックスが逆方向にスキャン中に、デスティネーションレジスタにロードされます。

(以下から抽出: http://dlc.Sun.com/pdf/802-1948/802-1948.pdf

そして、1よりも結果を増やします。

そう:

bsr ecx, eax  //eax = number
jz  @zero
mov eax, 2    // result set the second bit (instead of a inc ecx)
shl eax, ecx  // and move it ecx times to the left
ret           // result is in eax

@zero:
xor eax, eax
ret

新しいCPUでは、はるかに高速のlzcnt命令(別名rep bsr)。 lzcntは、単一のサイクルでジョブを実行します。

29
Davy Landman

より数学的な方法、ループなし:

public static int ByLogs(int n)
{
    double y = Math.Floor(Math.Log(n, 2));

    return (int)Math.Pow(2, y + 1);
}
21
DanDan

論理的な答えは次のとおりです。

function getK(int n)
{
  int k = 1;
  while (k < n)
    k *= 2;
  return k;
}
12
JustLoren

ループとして実装されたJohn Feminellaの答えは、次のとおりです Pythonの長整数

def next_power_of_2(n):
    """
    Return next power of 2 greater than or equal to n
    """
    n -= 1 # greater than OR EQUAL TO n
    shift = 1
    while (n+1) & n: # n+1 is not a power of 2 yet
        n |= n >> shift
        shift <<= 1
    return n + 1

また、nがすでに2の累乗である場合、より速く戻ります。

Python> 2.7の場合、これはほとんどのNでよりシンプルで高速です:

def next_power_of_2(n):
    """
    Return next power of 2 greater than or equal to n
    """
    return 2**(n-1).bit_length()

enter image description here

8
endolith

ループはありませんが、中間フロートを使用するワイルドなものがあります。

//  compute k = nextpowerof2(n)

if (n > 1) 
{
  float f = (float) n;
  unsigned int const t = 1U << ((*(unsigned int *)&f >> 23) - 0x7f);
  k = t << (t < n);
}
else k = 1;

これと、John Feminellaが提出したものを含む、他の多くのビットをいじるハックは、 here にあります。

3
I. J. Kennedy

より大きい/より大きい

次のスニペットは、次の数k> nであり、k = 2 ^ i
(n = 123 => k = 128、n = 128 => k = 256)OPで指定。

OR nと等しい最小の2のべき乗)が必要な場合は、__builtin_clzll(n)を置き換えるだけです。上記のスニペット内で__builtin_clzll(n-1)によって。

GCCまたはClangを使用したC++ 11(64ビット)

constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
    return 1ULL << (sizeof(uint64_t) * 8 - __builtin_clzll(n));
}

CHAR_BIT が提案した拡張機能 martinec

#include <cstdint>

constexpr uint64_t nextPowerOfTwo64 (uint64_t n)
{
    return 1ULL << (sizeof(uint64_t) * CHAR_BIT - __builtin_clzll(n));
}

GCCまたはClangを使用したC++ 17(8〜128ビット)

#include <cstdint>

template <typename T>
constexpr T nextPowerOfTwo64 (T n)
{
   T clz = 0;
   if constexpr (sizeof(T) <= 32)
      clz = __builtin_clzl(n); // unsigned long
   else if (sizeof(T) <= 64)
      clz = __builtin_clzll(n); // unsigned long long
   else { // See https://stackoverflow.com/a/40528716
      uint64_t hi = n >> 64;
      uint64_t lo = (hi == 0) ? n : -1ULL;
      clz = _lzcnt_u64(hi) + _lzcnt_u64(lo);
   }
   return T{1} << (CHAR_BIT * sizeof(T) - clz);
}

その他のコンパイラ

GCCまたはClang以外のコンパイラを使用する場合は、ウィキペディアのページにアクセスしてください Count Leading Zeroesbitwise functions

  • Visual C++ 2005 => __builtin_clzl()_BitScanForward()に置き換えます
  • Visual C++ 2008 => __builtin_clzl()__lzcnt()に置き換えます
  • icc => __builtin_clzl()_bit_scan_forwardに置き換えます
  • GHC(Haskell)=> __builtin_clzl()countLeadingZeros()に置き換え

寄付歓迎

コメント内で改善を提案してください。また、使用するコンパイラまたはプログラミング言語の代替案を提案してください...

同様の回答もご覧ください

3
olibre

xが負でないと仮定します。

int pot = Integer.highestOneBit(x);
if (pot != x) {
    pot *= 2;
}
2
Alex Cohn

GCC、MinGW、またはClangを使用する場合:

_template <typename T>
T nextPow2(T in)
{
  return (in & (T)(in - 1)) ? (1U << (sizeof(T) * 8 - __builtin_clz(in))) : in;
}
_

Microsoft Visual C++を使用している場合は、_BitScanForward()を置き換えるために関数__builtin_clz()を使用します。

2
nulleight
function Pow2Thing(int n)
{
    x = 1;
    while (n>0)
    {
        n/=2;
        x*=2;
    }
    return x;
}
1
Rik Heywood

ビットいじり、あなたは言う?

long int pow_2_ceil(long int t) {
    if (t == 0) return 1;
    if (t != (t & -t)) {
        do {
            t -= t & -t;
        } while (t != (t & -t));
        t <<= 1;
    }
    return t;
}

各ループは、最下位の1ビットを直接除去します。 N.B.これは、符号付き数値が2の補数でエンコードされている場合にのみ機能します。

1
Derek Illchuk
private static int nextHighestPower(int number){
    if((number & number-1)==0){
        return number;
    }
    else{
        int count=0;
        while(number!=0){
            number=number>>1;
            count++;
        }
        return 1<<count;
    }
}
0
lavish

このようなものはどうですか:

int pot = 1;
for (int i = 0; i < 31; i++, pot <<= 1)
    if (pot >= x)
        break;
0
Eric

これを忘れます!ループを使用します!

     unsigned int nextPowerOf2 ( unsigned int u)
     {
         unsigned int v = 0x80000000; // supposed 32-bit unsigned int

         if (u < v) {
            while (v > u) v = v >> 1;
         }
         return (v << 1);  // return 0 if number is too big
     }
0
nunkown

最上位ビットを見つけて、左に1回シフトするだけです。 Python実装です。x86にはMSBを取得する命令がありますが、ここではすべてをPythonで実装しています。MSBを取得したら簡単です。

>>> def msb(n):
...     result = -1
...     index = 0
...     while n:
...         bit = 1 << index
...         if bit & n:
...             result = index
...             n &= ~bit
...         index += 1
...     return result
...
>>> def next_pow(n):
...     return 1 << (msb(n) + 1)
...
>>> next_pow(1)
2
>>> next_pow(2)
4
>>> next_pow(3)
4
>>> next_pow(4)
8
>>> next_pow(123)
128
>>> next_pow(222)
256
>>>
0
FogleBird