Come si imposta, cancella e si alterna un po '?
Come si imposta, cancella e si alterna un po '?
Risposte:
Utilizzare l'operatore OR bit a bit ( |
) per impostare un bit.
number |= 1UL << n;
Questo imposterà il n
th di number
. n
dovrebbe essere zero, se si desidera impostare il 1
bit st e così via n-1
, se si desidera impostare il n
th bit.
Utilizzare 1ULL
se number
è più largo di unsigned long
; la promozione di 1UL << n
non avviene fino a dopo aver valutato 1UL << n
dove il comportamento indefinito si sposta di oltre la larghezza di a long
. Lo stesso vale per tutto il resto degli esempi.
Utilizzare l'operatore AND bit per bit ( &
) per cancellare un po '.
number &= ~(1UL << n);
Questo chiarirà il punto n
di number
. È necessario invertire la stringa di bit con l'operatore NOT bit a bit ( ~
), quindi AND.
L'operatore XOR ( ^
) può essere usato per alternare un po '.
number ^= 1UL << n;
Ciò commuterà il punto n
di number
.
Non hai chiesto questo, ma potrei anche aggiungerlo.
Per controllare un po ', sposta il numero n a destra, quindi bit a bit E esso:
bit = (number >> n) & 1U;
Ciò inserirà il valore del n
th bit di number
nella variabile bit
.
Impostare il n
th bit su uno 1
o 0
può essere ottenuto con quanto segue sull'implementazione C ++ di un complemento di 2:
number ^= (-x ^ number) & (1UL << n);
Il bit n
verrà impostato se x
è 1
, e cancellato se lo x
è 0
. Se x
ha qualche altro valore, ottieni spazzatura. x = !!x
lo booleanizzerà su 0 o 1.
Per renderlo indipendente dal comportamento di negazione del complemento di 2 (dove -1
sono impostati tutti i bit, diversamente dall'implementazione di un complemento di 1 o segno / magnitudine C ++), utilizzare la negazione senza segno.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
o
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
In genere è una buona idea utilizzare tipi senza segno per la manipolazione di bit portatile.
o
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
cancellerà il n
bit th e (x << n)
imposterà il n
bit th su x
.
In genere è anche una buona idea non copiare / incollare il codice in generale e così tante persone usano macro di preprocessore (come la wiki della community più in basso ) o una sorta di incapsulamento.
bit = (number >> x) & 1
1
è un int
letterale, che è firmato. Quindi tutte le operazioni qui operano su numeri firmati, che non è ben definito dagli standard. Gli standard non garantiscono il complemento a due o il cambiamento aritmetico, quindi è meglio usarli 1U
.
number = number & ~(1 << n) | (x << n);
cambiare il n-esimo bit in x.
Utilizzando la libreria standard C ++: std::bitset<N>
.
O il Boost versione: boost::dynamic_bitset
.
Non è necessario creare il tuo:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<5> x;
x[1] = 1;
x[2] = 0;
// Note x[0-4] valid
std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010
La versione Boost consente un bitset di dimensioni di runtime rispetto a un set di bit di dimensioni di compilazione della libreria standard .
L'altra opzione è utilizzare i campi bit:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
definisce un campo a 3 bit (in realtà, sono tre campi da 1 bit). Le operazioni sui bit ora diventano un po 'più semplici (ahah):
Per impostare o cancellare un po ':
mybits.b = 1;
mybits.c = 0;
Per alternare un po ':
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Verifica un po ':
if (mybits.c) //if mybits.c is non zero the next line below will execute
Funziona solo con campi bit di dimensioni fisse. Altrimenti devi ricorrere alle tecniche di manipolazione dei bit descritte nei post precedenti.
Uso le macro definite in un file di intestazione per gestire il set di bit e cancellare:
/* a=target variable, b=bit number to act upon 0-n */
#define BIT_SET(a,b) ((a) |= (1ULL<<(b)))
#define BIT_CLEAR(a,b) ((a) &= ~(1ULL<<(b)))
#define BIT_FLIP(a,b) ((a) ^= (1ULL<<(b)))
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1
/* x=target variable, y=mask */
#define BITMASK_SET(x,y) ((x) |= (y))
#define BITMASK_CLEAR(x,y) ((x) &= (~(y)))
#define BITMASK_FLIP(x,y) ((x) ^= (y))
#define BITMASK_CHECK_ALL(x,y) (((x) & (y)) == (y)) // warning: evaluates y twice
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
BITMASK_CHECK(x,y) ((x) & (y))
deve essere ((x) & (y)) == (y)
altrimenti restituisce un risultato errato sulla maschera multibit (es. 5
vs. 3
) / * Ciao a tutti i becchini
1
dovrebbe essere (uintmax_t)1
o simile nel caso in cui qualcuno cerchi di usare queste macro su un long
tipo o più grande
BITMASK_CHECK_ALL(x,y)
può essere implementato come!~((~(y))|(x))
!(~(x) & (y))
A volte vale la pena usare un enum
per nominare i bit:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Quindi utilizzare i nomi in seguito. Vale a dire
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
per impostare, cancellare e testare. In questo modo nascondi i numeri magici dal resto del codice.
A parte questo, approvo la soluzione di Jeremy.
clearbits()
funzione anziché &= ~
. Perché stai usando un enum per questo? Ho pensato che fossero per creare un gruppo di variabili uniche con valore arbitrario nascosto, ma stai assegnando un valore definito a ciascuna. Quindi qual è il vantaggio rispetto alla loro definizione come variabili?
enum
s per insiemi di costanti correlate risale a molto tempo fa nella programmazione c. Sospetto che con i compilatori moderni l'unico vantaggio rispetto const short
o comunque sia che siano esplicitamente raggruppati insieme. E quando li desideri per qualcosa di diverso dalle maschere di bit, ottieni la numerazione automatica. In c ++ ovviamente, formano anche tipi distinti che ti danno un po 'di extra controllo statico degli errori.
enum ThingFlags
valore ThingError|ThingFlag1
, ad esempio?
int
. Ciò può causare tutti i tipi di bug sottili a causa della promozione implicita di numeri interi o operazioni bit a bit su tipi firmati. thingstate = ThingFlag1 >> 1
invocherà ad esempio un comportamento definito dall'implementazione. thingstate = (ThingFlag1 >> x) << y
può invocare comportamenti indefiniti. E così via. Per sicurezza, esegui sempre il cast di un tipo senza segno.
enum My16Bits: unsigned short { ... };
/*
** Bit set, clear, and test operations
**
** public domain snippet by Bob Stout
*/
typedef enum {ERROR = -1, FALSE, TRUE} LOGICAL;
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
OK, analizziamo le cose ...
L'espressione comune con cui sembri avere problemi in tutti questi è "(1L << (posn))". Tutto ciò che fa è creare una maschera con un solo bit acceso e che funzionerà con qualsiasi tipo intero. L'argomento "posn" specifica la posizione in cui si desidera il bit. Se posn == 0, questa espressione valuterà:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Se posn == 8, valuterà:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
In altre parole, crea semplicemente un campo di 0 con un 1 nella posizione specificata. L'unica parte difficile è nella macro BitClr () in cui è necessario impostare un singolo 0 bit in un campo di 1. Ciò si ottiene utilizzando il complemento 1 della stessa espressione indicata dall'operatore tilde (~).
Una volta creata la maschera, viene applicata all'argomento proprio come suggerisci, usando gli operatori bit per bit e (&), o (|) e xor (^). Poiché la maschera è di tipo long, le macro funzioneranno altrettanto bene su char, short, int o long.
La linea di fondo è che questa è una soluzione generale per un'intera classe di problemi. Naturalmente, è possibile e persino appropriato riscrivere l'equivalente di una di queste macro con valori di maschera espliciti ogni volta che ne hai bisogno, ma perché farlo? Ricorda, la sostituzione della macro si verifica nel preprocessore e quindi il codice generato rifletterà il fatto che i valori sono considerati costanti dal compilatore - vale a dire utilizzare le macro generalizzate è altrettanto efficace che "reinventare la ruota" ogni volta che è necessario manipolare un po '.
Poco convinta? Ecco un po 'di codice di prova: ho usato Watcom C con ottimizzazione completa e senza usare _cdecl, quindi lo smontaggio risultante sarebbe il più pulito possibile:
---- [TEST.C] ----------------------------------------- -----------------------
#define BOOL(x) (!(!(x)))
#define BitSet(arg,posn) ((arg) | (1L << (posn)))
#define BitClr(arg,posn) ((arg) & ~(1L << (posn)))
#define BitTst(arg,posn) BOOL((arg) & (1L << (posn)))
#define BitFlp(arg,posn) ((arg) ^ (1L << (posn)))
int bitmanip(int word)
{
word = BitSet(word, 2);
word = BitSet(word, 7);
word = BitClr(word, 3);
word = BitFlp(word, 9);
return word;
}
---- [TEST.OUT (smontato)] -------------------------------------- ---------
Module: C:\BINK\tst.c
Group: 'DGROUP' CONST,CONST2,_DATA,_BSS
Segment: _TEXT BYTE 00000008 bytes
0000 0c 84 bitmanip_ or al,84H ; set bits 2 and 7
0002 80 f4 02 xor ah,02H ; flip bit 9 of EAX (bit 1 of AH)
0005 24 f7 and al,0f7H
0007 c3 ret
No disassembly errors
---- [finis] ------------------------------------------- ----------------------
arg
è long long
. 1L
deve essere il tipo più ampio possibile, quindi (uintmax_t)1
. (Potresti cavartela 1ull
)
Usa gli operatori bit a bit: &
|
Per impostare l'ultimo bit in 000b
:
foo = foo | 001b
Per controllare l'ultimo bit in foo
:
if ( foo & 001b ) ....
Per cancellare l'ultimo bit in foo
:
foo = foo & 110b
Ho usato XXXb
per chiarezza. Probabilmente lavorerai con la rappresentazione HEX, a seconda della struttura dei dati in cui impacchetti i bit.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Per il principiante vorrei spiegare un po 'di più con un esempio:
Esempio:
value is 0x55;
bitnum : 3rd.
L' &
operatore viene utilizzato controllare il bit:
0101 0101
&
0000 1000
___________
0000 0000 (mean 0: False). It will work fine if the third bit is 1 (then the answer will be True)
Attiva / Disattiva:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
operatore: imposta il bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Ecco la mia macro aritmetica di bit preferita, che funziona per qualsiasi tipo di array intero senza segno unsigned char
fino a size_t
(che è il tipo più grande con cui dovrebbe essere efficiente lavorare):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Per impostare un po ':
BITOP(array, bit, |=);
Per cancellare un po ':
BITOP(array, bit, &=~);
Per alternare un po ':
BITOP(array, bit, ^=);
Per testare un po ':
if (BITOP(array, bit, &)) ...
eccetera.
BITOP(array, bit++, |=);
in un ciclo molto probabilmente non farà ciò che vuole il chiamante.
BITCELL(a,b) |= BITMASK(a,b);
(entrambi prendono a
come argomento per determinare la dimensione, ma quest'ultimo non valuterà mai a
poiché appare solo in sizeof
).
(size_t)
cast sembra essere lì solo per assicurare una matematica non firmata con %
. Potrebbe (unsigned)
esserci.
(size_t)(b)/(8*sizeof *(a))
inutilmente potrebbe restringere b
prima della divisione. Solo un problema con array di bit molto grandi. Ancora una macro interessante.
Dato che questo è etichettato "incorporato", suppongo che stai usando un microcontrollore. Tutti i suggerimenti sopra riportati sono validi e funzionano (leggi-modifica-scrivi, sindacati, strutture, ecc.).
Tuttavia, durante un attacco di debug basato su oscilloscopio, sono rimasto sorpreso di scoprire che questi metodi hanno un notevole sovraccarico nei cicli della CPU rispetto alla scrittura di un valore direttamente nei registri PORTnSET / PORTnCLEAR del micro che fa la differenza in presenza di loop / high stretti -perni di attivazione / disattivazione di ISR.
Per quelli che non hanno familiarità: Nel mio esempio, il micro ha un registro di stato pin generale PORTn che riflette i pin di uscita, quindi facendo PORTn | = BIT_TO_SET si traduce in una lettura-modifica-scrittura in quel registro. Tuttavia, i registri PORTnSET / PORTnCLEAR prendono un '1' per indicare "si prega di rendere questo bit 1" (SET) o "si prega di rendere questo bit zero" (CLEAR) e uno '0' per indicare "lasciare il pin da solo". quindi, finisci con due indirizzi di porta a seconda che tu stia impostando o cancellando il bit (non sempre conveniente) ma una reazione molto più veloce e un codice assemblato più piccolo.
volatile
e quindi il compilatore non è in grado di eseguire alcuna ottimizzazione sul codice che coinvolge tali registri. Pertanto, è buona norma smontare tale codice e vedere come è risultato a livello di assemblatore.
L'approccio bitfield presenta altri vantaggi nell'arena integrata. È possibile definire una struttura che si associa direttamente ai bit in un particolare registro hardware.
struct HwRegister {
unsigned int errorFlag:1; // one-bit flag field
unsigned int Mode:3; // three-bit mode field
unsigned int StatusCode:4; // four-bit status code
};
struct HwRegister CR3342_AReg;
È necessario essere consapevoli dell'ordine di impacchettamento dei bit: penso che sia prima MSB, ma questo potrebbe dipendere dall'implementazione. Inoltre, verifica come i campi dei gestori del compilatore attraversano i limiti di byte.
È quindi possibile leggere, scrivere, testare i singoli valori come prima.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Esempio di utilizzo:
int main(void)
{
unsigned char arr[8] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF };
for (int ix = 0; ix < 64; ++ix)
printf("bit %d is %d\n", ix, bit_test(arr, ix));
return 0;
}
Note: Questo è progettato per essere veloce (data la sua flessibilità) e non ramoso. Il risultato è un codice macchina SPARC efficiente durante la compilazione di Sun Studio 8; L'ho anche testato usando MSVC ++ 2008 su amd64. È possibile creare macro simili per impostare e cancellare i bit. La differenza chiave di questa soluzione rispetto a molte altre qui è che funziona per qualsiasi posizione praticamente in qualsiasi tipo di variabile.
Più generale, per bitmap di dimensioni arbitrarie:
#define BITS 8
#define BIT_SET( p, n) (p[(n)/BITS] |= (0x80>>((n)%BITS)))
#define BIT_CLEAR(p, n) (p[(n)/BITS] &= ~(0x80>>((n)%BITS)))
#define BIT_ISSET(p, n) (p[(n)/BITS] & (0x80>>((n)%BITS)))
CHAR_BIT
è già definito da limits.h
, non è necessario inserire il proprio BITS
(e in effetti peggiori il tuo codice nel farlo)
Questo programma consente di modificare qualsiasi bit di dati da 0 a 1 o da 1 a 0:
{
unsigned int data = 0x000000F0;
int bitpos = 4;
int bitvalue = 1;
unsigned int bit = data;
bit = (bit>>bitpos)&0x00000001;
int invbitvalue = 0x00000001&(~bitvalue);
printf("%x\n",bit);
if (bitvalue == 0)
{
if (bit == 0)
printf("%x\n", data);
else
{
data = (data^(invbitvalue<<bitpos));
printf("%x\n", data);
}
}
else
{
if (bit == 1)
printf("elseif %x\n", data);
else
{
data = (data|(bitvalue<<bitpos));
printf("else %x\n", data);
}
}
}
Se stai facendo un po 'di manipolazione, potresti voler usare maschere che renderanno il tutto più veloce. Le seguenti funzioni sono molto veloci e sono ancora flessibili (consentono di modificare i bit in bitmap di qualsiasi dimensione).
const unsigned char TQuickByteMask[8] =
{
0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80,
};
/** Set bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TSetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] |= TQuickByteMask[n]; // Set bit.
}
/** Reset bit in any sized mask.
*
* @return None
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TResetBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] &= (~TQuickByteMask[n]); // Reset bit.
}
/** Toggle bit in any sized bit mask.
*
* @return none
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
void TToggleBit( short bit, unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
bitmap[x] ^= TQuickByteMask[n]; // Toggle bit.
}
/** Checks specified bit.
*
* @return 1 if bit set else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitSet( short bit, const unsigned char *bitmap)
{
short n, x;
x = bit / 8; // Index to byte.
n = bit % 8; // Specific bit in byte.
// Test bit (logigal AND).
if (bitmap[x] & TQuickByteMask[n])
return 1;
return 0;
}
/** Checks specified bit.
*
* @return 1 if bit reset else 0.
*
* @param bit - Bit number.
* @param bitmap - Pointer to bitmap.
*/
short TIsBitReset( short bit, const unsigned char *bitmap)
{
return TIsBitSet(bit, bitmap) ^ 1;
}
/** Count number of bits set in a bitmap.
*
* @return Number of bits set.
*
* @param bitmap - Pointer to bitmap.
* @param size - Bitmap size (in bits).
*
* @note Not very efficient in terms of execution speed. If you are doing
* some computationally intense stuff you may need a more complex
* implementation which would be faster (especially for big bitmaps).
* See (http://graphics.stanford.edu/~seander/bithacks.html).
*/
int TCountBits( const unsigned char *bitmap, int size)
{
int i, count = 0;
for (i=0; i<size; i++)
if (TIsBitSet(i, bitmap))
count++;
return count;
}
Nota, per impostare il bit 'n' in un numero intero a 16 bit, procedi come segue:
TSetBit( n, &my_int);
Sta a te assicurarti che il numero di bit sia compreso nell'intervallo della mappa di bit che passi. Nota che per i processori little endian che byte, parole, dwords, qwords, ecc., Si mappano correttamente l'uno con l'altro in memoria (motivo principale per cui i processori little endian sono "migliori" dei processori big endian, ah, sento che sta arrivando una guerra di fiamma su...).
Usa questo:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Espandendo sulla bitset
risposta:
#include <iostream>
#include <bitset>
#include <string>
using namespace std;
int main() {
bitset<8> byte(std::string("10010011");
// Set Bit
byte.set(3); // 10010111
// Clear Bit
byte.reset(2); // 10010101
// Toggle Bit
byte.flip(7); // 00010101
cout << byte << endl;
return 0;
}
Se si desidera eseguire tutte queste operazioni con la programmazione C nel kernel Linux, suggerisco di utilizzare le API standard del kernel Linux.
Vedi https://www.kernel.org/doc/htmldocs/kernel-api/ch02s03.html
set_bit Atomically set a bit in memory
clear_bit Clears a bit in memory
change_bit Toggle a bit in memory
test_and_set_bit Set a bit and return its old value
test_and_clear_bit Clear a bit and return its old value
test_and_change_bit Change a bit and return its old value
test_bit Determine whether a bit is set
Nota: qui l'intera operazione avviene in un unico passaggio. Quindi, tutti questi sono garantiti per essere atomici anche sui computer SMP e sono utili per mantenere la coerenza tra i processori.
Visual C 2010, e forse molti altri compilatori, hanno il supporto diretto per le operazioni booleane integrate. Un po 'ha due possibili valori, proprio come un booleano, quindi possiamo usare i booleani, anche se occupano più spazio di un singolo bit in memoria in questa rappresentazione. Questo funziona, anche l' sizeof()
operatore funziona correttamente.
bool IsGph[256], IsNotGph[256];
// Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
Quindi, alla tua domanda, IsGph[i] =1
o IsGph[i] =0
semplifica l'impostazione e l'eliminazione dei bool.
Per trovare caratteri non stampabili:
// Initialize boolean array to detect UN-printable characters,
// then call function to toggle required bits true, while initializing a 2nd
// boolean array as the complement of the 1st.
for(i=0; i<sizeof(IsGph); i++) {
if(IsGph[i]) {
IsNotGph[i] = 0;
} else {
IsNotGph[i] = 1;
}
}
Nota che non c'è nulla di "speciale" in questo codice. Tratta un po 'come un intero - che tecnicamente lo è. Un numero intero a 1 bit che può contenere solo 2 valori e 2 valori.
Una volta ho usato questo approccio per trovare record di prestito duplicati, dove loan_number era la chiave ISAM, usando il numero di prestito a 6 cifre come indice nell'array di bit. Rapidamente veloce, e dopo 8 mesi, ha dimostrato che il sistema mainframe da cui stavamo ottenendo i dati era in effetti mal funzionante. La semplicità delle matrici di bit rende la fiducia nella loro correttezza molto elevata, ad esempio rispetto a un approccio di ricerca.
bool
. Forse anche 4 byte per le configurazioni C89 che usano int
per implementarebool
Ecco alcune macro che uso:
SET_FLAG(Status, Flag) ((Status) |= (Flag))
CLEAR_FLAG(Status, Flag) ((Status) &= ~(Flag))
INVALID_FLAGS(ulFlags, ulAllowed) ((ulFlags) & ~(ulAllowed))
TEST_FLAGS(t,ulMask, ulBit) (((t)&(ulMask)) == (ulBit))
IS_FLAG_SET(t,ulMask) TEST_FLAGS(t,ulMask,ulMask)
IS_FLAG_CLEAR(t,ulMask) TEST_FLAGS(t,ulMask,0)
Variabile utilizzata
int value, pos;
valore - Dati
pos - posizione del bit che ci interessa impostare, cancellare o attivare.
Imposta un po ':
value = value | 1 << pos;
Cancella un po ':
value = value & ~(1 << pos);
Attiva un po ':
value = value ^ 1 << pos;
int set_nth_bit(int num, int n){
return (num | 1 << n);
}
int clear_nth_bit(int num, int n){
return (num & ~( 1 << n));
}
int toggle_nth_bit(int num, int n){
return num ^ (1 << n);
}
int check_nth_bit(int num, int n){
return num & (1 << n);
}
check_nth_bit
può essere bool
.
Supponiamo che prima
num = 55
intere poche operazioni eseguano operazioni bit per bit (imposta, ottieni, cancella, attiva / disattiva).
n = 4
Posizione bit basata su 0 per eseguire operazioni bit a bit.
nth
bit di num shift giusto num
, n
volte. Quindi eseguire bit AND AND &
con 1.bit = (num >> n) & 1;
Come funziona?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
volte. Quindi eseguire l' |
operazione OR bit a bit con num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Come funziona?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
volte ie 1 << n
.~ (1 << n)
.&
operazione AND bit per bit con il risultato sopra e num
. I tre passaggi precedenti possono essere scritti come num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Come funziona?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Per alternare un po 'usiamo XOR bit a bit ^
operatore . L'operatore XOR bit a bit restituisce 1 se il bit corrispondente di entrambi gli operandi è diverso, altrimenti a 0.
Ciò significa che è necessario attivare o disattivare un po ', è necessario eseguire l'operazione XOR con il bit che si desidera attivare e 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Come funziona?
0 ^ 1 => 1
. 1 ^ 1 => 0
. 0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
^ 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Lettura consigliata - Esercitazioni bit per operatore
Come si imposta, cancella e si attiva / disattiva un singolo bit?
Affrontare un errore comune nella codifica quando si tenta di formare la maschera:
1
non è sempre abbastanza ampio
Quali problemi si verificano quando number
è un tipo più ampio di 1
?
x
potrebbe essere troppo grande per il turno che 1 << x
porta a comportamenti indefiniti (UB). Anche se x
non è troppo grande, ~
potrebbe non capovolgere abbastanza bit più significativi.
// assume 32 bit int/unsigned
unsigned long long number = foo();
unsigned x = 40;
number |= (1 << x); // UB
number ^= (1 << x); // UB
number &= ~(1 << x); // UB
x = 10;
number &= ~(1 << x); // Wrong mask, not wide enough
Per assicurarsi che 1 sia abbastanza largo:
Il codice potrebbe usare 1ull
o pedanticamente (uintmax_t)1
e consentire al compilatore di ottimizzare.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
O cast - che rende problemi di codifica / revisione / manutenzione mantenendo il cast corretto e aggiornato.
number |= (type_of_number)1 << x;
O promuovi delicatamente 1
forzando un'operazione matematica che è larga almeno quanto il tipo di number
.
number |= (number*0 + 1) << x;
Come la maggior parte manipolazioni bit, meglio lavorare con unsigned tipi piuttosto che sottoscritti quelli
number |= (type_of_number)1 << x;
né number |= (number*0 + 1) << x;
opportuno impostare il bit di segno di un tipo firmato ... È un dato di fatto, nessuno dei due è number |= (1ull << x);
. Esiste un modo portatile per farlo in base alla posizione?
Una versione modello C ++ 11 (inserita in un'intestazione):
namespace bit {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bit) {variable |= ((T1)1 << bit);}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bit) {variable &= ~((T1)1 << bit);}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bit) {variable ^= ((T1)1 << bit);}
template <typename T1, typename T2> inline bool test (T1 &variable, T2 bit) {return variable & ((T1)1 << bit);}
}
namespace bitmask {
template <typename T1, typename T2> inline void set (T1 &variable, T2 bits) {variable |= bits;}
template <typename T1, typename T2> inline void clear(T1 &variable, T2 bits) {variable &= ~bits;}
template <typename T1, typename T2> inline void flip (T1 &variable, T2 bits) {variable ^= bits;}
template <typename T1, typename T2> inline bool test_all(T1 &variable, T2 bits) {return ((variable & bits) == bits);}
template <typename T1, typename T2> inline bool test_any(T1 &variable, T2 bits) {return variable & bits;}
}
;
dopo le definizioni delle tue funzioni?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
in c ++ 11
Questo programma è basato sulla soluzione precedente di @ Jeremy. Se qualcuno desidera giocare rapidamente.
public class BitwiseOperations {
public static void main(String args[]) {
setABit(0, 4); // set the 4th bit, 0000 -> 1000 [8]
clearABit(16, 5); // clear the 5th bit, 10000 -> 00000 [0]
toggleABit(8, 4); // toggle the 4th bit, 1000 -> 0000 [0]
checkABit(8,4); // check the 4th bit 1000 -> true
}
public static void setABit(int input, int n) {
input = input | ( 1 << n-1);
System.out.println(input);
}
public static void clearABit(int input, int n) {
input = input & ~(1 << n-1);
System.out.println(input);
}
public static void toggleABit(int input, int n) {
input = input ^ (1 << n-1);
System.out.println(input);
}
public static void checkABit(int input, int n) {
boolean isSet = ((input >> n-1) & 1) == 1;
System.out.println(isSet);
}
}
Output :
8
0
0
true
Prova una di queste funzioni nel linguaggio C per cambiare n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
O
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
O
void chang_n_bit(int n, int value)
{
if(value)
bitfield |= 1 << n;
else
bitfield &= ~0 ^ (1 << n);
}
char get_n_bit(int n)
{
return (bitfield & (1 << n)) ? 1 : 0;
}
value << n
può causare un comportamento indefinito