Qual è il modo più semplice per verificare se un numero è una potenza di 2 in C ++?


94

Ho bisogno di una funzione come questa:

// return true iff 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  is_power_of_2(3) => false
bool is_power_of_2(int n);

Qualcuno può suggerire come potrei scrivere questo? Puoi dirmi un buon sito web in cui è possibile trovare questo tipo di algoritmo?



@rootTraveller - Probabilmente non è un duplicato. C ++ e Java sono linguaggi diversi e ognuno offre servizi diversi. Ad esempio, in C / C ++ ora possiamo usare gli intrinseci con processori abilitati BMI, che emette l'istruzione della macchina per farlo in una volta sola. Immagino che Java abbia altre cose, come forse qualcosa di una routine matematica.
jww

Risposte:


190

(n & (n - 1)) == 0è meglio. Tuttavia, nota che restituirà erroneamente true per n = 0, quindi se ciò è possibile, dovrai verificarlo esplicitamente.

http://www.graphics.stanford.edu/~seander/bithacks.html ha una vasta collezione di algoritmi intelligenti, compreso questo.


8
quindi fondamentalmente(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal o n && !(n & (n - 1))come il collegamento all'interno della risposta afferma.
Carsten

Perché, oh perché, non è questo in cima alle risposte? OP per favore accetta.
donturner

@SaurabhGoyal Un piccolo miglioramento è questo: n & !(n & (n - 1)). Notare l'AND bit per bit &(non logico e &&). Gli operatori bit per bit non implementano il cortocircuito e, quindi, il codice non si ramifica. Ciò è preferibile in situazioni in cui sono probabili previsioni errate di branch e quando il calcolo della rh dell'espressione (cioè !(n & (n - 1))) è economico.
Cassio Neri

@cassio !è un operatore logico e quindi il valore di !(n & (n - 1))sarebbe un booleano. Sei sicuro che un booleano e un numero possono essere dati a un operatore AND bit per bit? Se sì, sembra buono.
Saurabh Goyal

81

Una potenza di due avrà un solo bit impostato (per i numeri senza segno). Qualcosa di simile a

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

Funzionerà bene; uno in meno di una potenza di due è tutto 1 nei bit meno significativi, quindi deve AND a 0 bit per bit.

Dato che stavo assumendo numeri senza segno, il test == 0 (che originariamente avevo dimenticato, scusa) è adeguato. Potresti volere un test> 0 se stai usando numeri interi con segno.


Ti manca un "!" o un "== 0"

Ti manca anche un test per il valore negativo di x.
Rob Wells,

Bene, come hai fatto a modificarlo senza che appaia "modificato x minuti fa"?

Seriamente, come hai fatto a ottenere 120 ripetizioni per una risposta palesemente sbagliata?

@ Mike F: In effetti, sembra che le persone voteranno le risposte senza controllarle. Chiunque può commettere un errore, immagino - se ne farò uno in futuro, sentiti libero di modificarli.
Adam Wright,

49

I poteri di due in formato binario hanno questo aspetto:

1: 0001
2: 0010
4: 0100
8: 1000

Notare che è sempre impostato esattamente 1 bit. L'unica eccezione è con un numero intero con segno. Ad esempio, un intero con segno a 8 bit con un valore di -128 ha il seguente aspetto:

10000000

Quindi, dopo aver verificato che il numero sia maggiore di zero, possiamo usare un piccolo trucco intelligente per testare che uno e solo un bit è impostato.

bool is_power_of_2(int x) {
    return x > 0 && !(x & (x1));
}

Per ulteriori dettagli, vedere qui .


14

Approccio n. 1:

Dividi il numero per 2 in modo recluso per controllarlo.

Complessità temporale: O (log2n).

Approccio n. 2:

Bitwise AND il numero con il suo numero appena precedente dovrebbe essere uguale a ZERO.

Esempio: Numero = 8 Binario di 8: 1 0 0 0 Binario di 7: 0 1 1 1 e l'AND bit per bit di entrambi i numeri è 0 0 0 0 = 0.

Complessità temporale: O (1).

Approccio n. 3:

Bitwise XOR il numero con il suo numero appena precedente dovrebbe essere la somma di entrambi i numeri.

Esempio: Numero = 8 Binario di 8: 1 0 0 0 Binario di 7: 0 1 1 1 e lo XOR bit per bit di entrambi i numeri è 1 1 1 1 = 15.

Complessità temporale: O (1).

http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html


8
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}

7

per ogni potenza di 2 vale anche quanto segue.

n & (- n) == n

NOTA: la condizione è vera per n = 0, sebbene non sia una potenza di 2. Il
motivo per cui funziona è:
-n è il complemento a 2s di n. -n avrà ogni bit a sinistra del bit impostato più a destra di n capovolto rispetto a n. Per potenze di 2 c'è solo un bit impostato.


2
volevo dire che la condizione è vera per n = 0 sebbene non sia un potere di due
FReeze FRancis

funziona con le conversioni che si verificano se n non è firmato?
Joseph Garvin

5

In C ++ 20 c'è std::ispow2che puoi usare esattamente per questo scopo se non hai bisogno di implementarlo da solo:

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));

5

Questo è probabilmente il più veloce, se si utilizza GCC. Utilizza solo un'istruzione della cpu POPCNT e un confronto. La rappresentazione binaria di qualsiasi potenza di 2 numeri, ha sempre un solo bit impostato, gli altri bit sono sempre zero. Quindi contiamo il numero di bit impostati con POPCNT, e se è uguale a 1, il numero è una potenza di 2. Non penso che ci siano metodi più veloci possibili. Ed è molto semplice, se l'hai capito una volta:

if(1==__builtin_popcount(n))

No. L'ho appena provato. Adoro il popcount ma per il test power-of-2, il test i && !(i & (i - 1)))è circa il 10% più veloce sulla mia macchina, anche quando ero sicuro di abilitare l'istruzione POPCNT dell'assembly nativo in gcc.
eraoul

Oops lo riprendo. Il mio programma di test era in esecuzione in un ciclo e la previsione dei rami era "barare". Hai ragione, se hai l'istruzione POPCNT sulla tua CPU è più veloce.
eraoul

3

La seguente sarebbe più veloce della risposta più votata a causa del cortocircuito booleano e del fatto che il confronto è lento.

int isPowerOfTwo(unsigned int x)
{
  return x && !(x & (x  1));
}

Se sai che x non può essere 0 allora

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x  1));
}


3

Qual è il modo più semplice per verificare se un numero è una potenza di 2 in C ++?

Se si dispone di un moderno processore Intel con le istruzioni per la manipolazione dei bit , è possibile eseguire quanto segue. Omette il codice C / C ++ diretto perché altri hanno già risposto, ma ne hai bisogno se BMI non è disponibile o abilitato.

bool IsPowerOf2_32(uint32_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u32(x));
#endif
    // Fallback to C/C++ code
}

bool IsPowerOf2_64(uint64_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u64(x));
#endif
    // Fallback to C/C++ code
}

GCC, ICC e supporto BMI del segnale Clang con __BMI__. È disponibile nei compilatori Microsoft in Visual Studio 2015 e versioni successive quando AVX2 è disponibile e abilitato . Per le intestazioni di cui hai bisogno, consulta File di intestazione per gli elementi intrinseci SIMD .

Di solito proteggo il _blsr_u64con una _LP64_compilazione del caso su i686. Clang ha bisogno di una piccola soluzione alternativa perché utilizza un simbolo intrinseco leggermente diverso nam:

#if defined(__GNUC__) && defined(__BMI__)
# if defined(__clang__)
#  ifndef _tzcnt_u32
#   define _tzcnt_u32(x) __tzcnt_u32(x)
#  endif
#  ifndef _blsr_u32
#    define  _blsr_u32(x)  __blsr_u32(x)
#  endif
#  ifdef __x86_64__
#   ifndef _tzcnt_u64
#    define _tzcnt_u64(x) __tzcnt_u64(x)
#   endif
#   ifndef _blsr_u64
#     define  _blsr_u64(x)  __blsr_u64(x)
#   endif
#  endif  // x86_64
# endif  // Clang
#endif  // GNUC and BMI

Puoi dirmi un buon sito web in cui è possibile trovare questo tipo di algoritmo?

Questo sito web è spesso citato: Bit Twiddling Hacks .


Questo non è certamente il "modo più semplice" come richiesto nell'OP, ma probabilmente il più veloce per ambienti specifici. Mostrare come condizionare per diverse architetture è estremamente utile.
fearless_fool

1

Questo non è il modo più veloce o più breve, ma penso che sia molto leggibile. Quindi farei qualcosa del genere:

bool is_power_of_2(int n)
  int bitCounter=0;
  while(n) {
    if ((n & 1) == 1) {
      ++bitCounter;
    }
    n >>= 1;
  }
  return (bitCounter == 1);
}

Questo funziona poiché il binario si basa su potenze di due. Qualsiasi numero con un solo bit impostato deve essere una potenza di due.


Potrebbe non essere veloce o breve, ma è corretto a differenza delle risposte migliori.

2
Al momento del commento erano tutti infastiditi. Da allora sono stati modificati in uno stato accettabile.

0

Ecco un altro metodo, in questo caso utilizzando |invece di &:

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}

0

È possibile tramite c ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}

2
Non è né semplice, né veloce, per me.
luk32

2
Cioè non è certamente veloce a causa di log2, e la prova che funziona non è così facile da spiegare (precisamente, puoi farti prendere da errori di arrotondamento?). È anche inutilmente contorto con if..return..else..return. Cosa c'è di sbagliato nel collassarlo return x==(double)y;? Dovrebbe restituire boolcomunque. IMO anche l'operatore ternario sarebbe più chiaro se si vuole davvero attenersi int.
luk32

0

So che questo è un post molto vecchio, ma ho pensato che potesse essere interessante pubblicarlo qui.


Da Code-Golf SE (quindi tutto il merito a chi l'ha scritto): Showcase of Languages

(Paragrafo su C , snippet Lunghezza 36 sottoparagrafo )

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

-1

Un altro modo (forse non il più veloce) è determinare se ln (x) / ln (2) è un numero intero.


2
Forse non c'è niente al riguardo :-).
paxdiablo

1
Ciò avrebbe problemi con l'imprecisione in virgola mobile. ln (1 << 29) / ln (2) esce a 29.000000000000004.
Anonimo

-3

Questo è il metodo bit-shift in T-SQL (SQL Server):

SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo

È molto più veloce che eseguire un logaritmo quattro volte (il primo set per ottenere il risultato decimale, il secondo set per ottenere il set intero e il confronto)


5
È bello vedere come la risposta principale a questa domanda possa essere implementata anche in T-SQL, ma ciò non è realmente rilevante per la domanda posta qui. Un'alternativa (se stavi cercando una soluzione in T-SQL, hai trovato questa domanda con risposta, l'hai implementata in T-SQL e hai pensato che fosse abbastanza interessante da pubblicare questa risposta) sarebbe quella di pubblicare la domanda con riferimento a T-SQL, quindi rispondi tu stesso, facendo riferimento a questa domanda con risposta. Spero che questo suggerimento sia utile.
Simon

questo non risponde davvero a questa domanda
phuclv
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.