web-dev-qa-db-fra.com

Étant donné un entier, comment puis-je trouver la deuxième puissance la plus élevée de deux en utilisant le twiddling de bits?

Si j'ai un nombre entier n, comment puis-je trouver le numéro suivant k > n tel que k = 2^i, avec un élément i de N par décalage binaire ou logique.

Exemple: si j'ai n = 123, Comment puis-je trouver k = 128, qui est une puissance de deux, et non 124 qui n'est divisible que par deux. Cela devrait être simple, mais cela m'échappe.

72
AndreasT

Pour les entiers 32 bits, il s'agit d'un itinéraire simple et direct:

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.

Voici un exemple plus concret. Prenons le nombre 221, qui est 11011101 en binaire:

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

Il y a un bit en neuvième position, ce qui représente 2 ^ 8, ou 256, qui est en effet la deuxième puissance la plus importante de 2. Chacun des décalages chevauche tous les 1 bits existants du nombre avec certains des zéros précédemment intacts, produisant finalement un nombre de 1 bits égal au nombre de bits du nombre d'origine. Ajouter un à cette valeur produit un nouveau pouvoir de 2.

Un autre exemple; nous utiliserons 131, qui est 10000011 en binaire:

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

Et en effet, 256 est la deuxième puissance la plus élevée de 2 sur 131.

Si le nombre de bits utilisés pour représenter l'entier est lui-même une puissance de 2, vous pouvez continuer à étendre cette technique efficacement et indéfiniment (par exemple, ajoutez un n >> 32 ligne pour les entiers 64 bits).

94
John Feminella

Il existe en fait une solution d'assemblage pour cela (depuis le jeu d'instructions 80386).

Vous pouvez utiliser l'instruction BSR (Bit Scan Reverse) pour rechercher le bit le plus significatif de votre entier.

bsr scanne les bits, en commençant par le bit le plus significatif, dans l'opérande double mot ou le deuxième mot. Si les bits sont tous nuls, ZF est effacé. Sinon, ZF est défini et l'index de bit du premier bit défini trouvé, pendant le balayage dans le sens inverse, est chargé dans le registre de destination

(Extrait de: http://dlc.Sun.com/pdf/802-1948/802-1948.pdf )

Et puis augmenter le résultat avec 1.

donc:

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

Dans les CPU plus récents, vous pouvez utiliser l'instruction lzcnt beaucoup plus rapide (aka rep bsr). lzcnt fait son travail en un seul cycle.

29
Davy Landman

Une manière plus mathématique, sans boucles:

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

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

Voici une réponse logique:

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

Voici la réponse de John Feminella implémentée en boucle afin qu'il puisse gérer les longs entiers de 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

Il revient également plus rapidement si n est déjà une puissance de 2.

Pour Python> 2.7, c'est plus simple et plus rapide pour la plupart des 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

Voici un joker qui n'a pas de boucles, mais utilise un flotteur intermédiaire.

//  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;

Ceci, ainsi que de nombreux autres hacks bidirectionnels, y compris celui soumis par John Feminella, peuvent être trouvés ici .

3
I. J. Kennedy

Supérieur à/Supérieur ou égal à

Les extraits suivants sont pour le nombre suivant k> n tel que k = 2 ^ i
(n = 123 => k = 128, n = 128 => k = 256) comme spécifié par OP.

Si vous voulez que la puissance la plus petite de 2 soit supérieure à OR égale à n , remplacez simplement __builtin_clzll(n) par __builtin_clzll(n-1) dans les extraits ci-dessus.

C++ 11 utilisant GCC ou Clang (64 bits)

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

Amélioration utilisant CHAR_BIT comme proposé par martinec

#include <cstdint>

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

C++ 17 utilisant GCC ou Clang (de 8 à 128 bits)

#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);
}

Autres compilateurs

Si vous utilisez un compilateur autre que GCC ou Clang, veuillez visiter la page Wikipedia répertoriant les nombre de zéros non significatifs fonctions au niveau du bit :

  • Visual C++ 2005 => Remplacer __builtin_clzl() par _BitScanForward()
  • Visual C++ 2008 => Remplacer __builtin_clzl() par __lzcnt()
  • icc => Remplacer __builtin_clzl() par _bit_scan_forward
  • GHC (Haskell) => Remplacer __builtin_clzl() par countLeadingZeros()

Bienvenue à la contribution

Veuillez proposer des améliorations dans les commentaires. Proposez également une alternative pour le compilateur que vous utilisez, ou votre langage de programmation ...

Voir aussi des réponses similaires

3
olibre

supposons que x n'est pas négatif.

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

Si vous utilisez GCC, MinGW ou Clang:

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

Si vous utilisez Microsoft Visual C++, utilisez la fonction _BitScanForward() pour remplacer __builtin_clz().

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

Bit-twiddling, vous dites?

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;
}

Chaque boucle supprime directement le bit le moins significatif. N.B. Cela ne fonctionne que lorsque les numéros signés sont codés en complément à deux.

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

Et quelque chose comme ça:

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

Oublie ça! Il utilise la boucle!

     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

Il vous suffit de trouver le bit le plus significatif et de le déplacer vers la gauche une fois. Voici une implémentation Python. Je pense que x86 a une instruction pour obtenir le MSB, mais ici je l'implémente en Python direct. Une fois que vous avez le MSB, c'est facile.

>>> 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