Cum setați, ștergeți și comutați puțin?
Cum setați, ștergeți și comutați un singur bit?
Răspunsuri (27)
Stabilirea un pic
Utilizați operatorul SAU pe biți (|) pentru a seta un bit.
number |= 1UL << n;
Aceasta va seta al nlea bit din number. n ar trebui să fie zero, dacă doriți să setați 1st bit și așa mai departe până la n-1, dacă doriți să setați nth bit.
Folosiți 1ULL dacă number este mai lat decât unsigned long; promovarea lui 1UL << n nu are loc decât după evaluarea lui 1UL << n în cazul în care comportamentul nedefinit se schimbă cu mai mult decât lățimea unui long. Același lucru este valabil și pentru toate celelalte exemple.
Curățind puțin
Utilizați operatorul AND pe biți (&) pentru a șterge puțin.
number &= ~(1UL << n);
Asta va șterge al nal-lea bit din number. Trebuie să inversați șirul de biți cu operatorul NOT pe biți (~), apoi AND it.
Comutând puțin
Operatorul XOR (^) poate fi folosit pentru a comuta puțin.
number ^= 1UL << n;
Aceasta va comuta al nal-lea bit din number.
Verifică puțin
Nu ai cerut asta, dar aș putea la fel de bine să-l adaug.
Pentru a verifica puțin, mutați numărul n la dreapta, apoi pe biți și acesta:
bit = (number >> n) & 1U;
Aceasta va pune valoarea celui de-al 25-lea bit al lui number în variabila bit.
Schimbarea nal-lea bit în x
Setarea celui de-al 28_lea bit la 1 sau la 0 poate fi realizată cu următoarele pe o implementare C++ în complement a 2:
number ^= (-x ^ number) & (1UL << n);
Bitul n va fi setat dacă x este 1 și șters dacă x este 0. Dacă x are o altă valoare, primești gunoi. x = !!x îl va booleaniza la 0 sau 1.
Pentru a face acest lucru independent de comportamentul de negație al complementului 2 (unde -1 are toți biții setați, spre deosebire de implementarea complementului 1 sau C++ cu semn/magnitudine), utilizați negația fără semn.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
or
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
În general, este o idee bună să folosiți tipuri nesemnate pentru manipularea biților portabili.
or
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n)) va șterge bitul nși (x << n) va seta bitul nla x.
De asemenea, este, în general, o idee bună să nu copiați/lipiți codul în general și atât de mulți oameni folosesc macrocomenzi de preprocesor (cum ar fi răspunsul wiki comunității mai jos) sau un fel de încapsulare.
number este mai lat decât int
- person anatolyg; 09.05.2013
bit = (number >> x) & 1
- person aaronman; 26.06.2013
1 este un literal int, care este semnat. Deci toate operațiunile de aici funcționează pe numere semnate, ceea ce nu este bine definit de standarde. Standardele nu garantează complementul în doi sau schimbarea aritmetică, așa că este mai bine să folosiți 1U.
- person Siyuan Ren; 10.12.2013
unsigned long long oricum... pot exista extensii definite de implementare, cum ar fi __int128 . Pentru a fi ultra-sigur (uintmax_t)1 << x
- person M.M; 07.02.2015
number = number & ~(1 << n) | (x << n); pentru schimbarea bitului n-lea la x.
- person jiasli; 24.03.2015
number este mai mare decât int și n este mai mare sau egal cu numărul de biți dintr-un int. Invocă chiar un comportament nedefinit în acest caz. number = number & ~((uintmax_t)1 << n) | ((uintmax_t)x << n); este o expresie generică care ar trebui să funcționeze pentru toate dimensiunile de number, dar poate genera cod urât și ineficient pentru dimensiuni mai mici.
- person chqrlie; 24.03.2015
-x nu este sigură, deoarece standardul C permite numerelor întregi cu semn să fie (de exemplu) mărimea semnului, complementul celor sau orice alt sistem care poate exprima intervalul necesar, ceea ce înseamnă că -x este nu trebuie să fie la fel ca (~x) + 1. Aceasta nu este o afacere atât de mare pentru arhitecturile moderne, dar nu știi niciodată ce va face un compilator de optimizare suficient de inteligent cu codul tău.
- person Kevin; 18.01.2016
~ în loc de !?
- person 71GA; 30.01.2017
~) inversează toți biții, deci ~0xFFF0FFFF este 0x000F0000. Boolean not (!) dă 0 dacă valoarea este diferită de zero sau 1 dacă valoarea este zero, deci !0xFFF0FFFF este 0x00000000.
- person Adrian McCarthy; 05.09.2017
-1 este 0b1111..1110. (Toate cele este -0). C++ permite, de asemenea, reprezentări semn/magnitudine întregi, unde -x doar răsturnează bitul de semn. Am actualizat acest răspuns pentru a sublinia asta. Este foarte bine dacă țintiți doar implementările C++ complementare 2. Nu este UB, este doar definit de implementare, deci este necesar să funcționeze corect pe implementări în care definesc numerele întregi cu semn ca complement 2. De asemenea, am schimbat constantele la 1UL, cu o notă că 1ULL poate fi necesar.
- person Peter Cordes; 10.11.2017
-x este UB dacă x este INT_MIN.
- person Kevin; 10.11.2017
-(unsigned long)x, ceea ce ar ocoli reprezentarea întregului semnat. (baza 2 fără semn se potrivește cu semantica complementului 2.) Dar funcționează corect numai dacă x este 0 sau 1. Amintiți-vă că setăm bitul n la x.
- person Peter Cordes; 11.11.2017
0U - 1U -› all-one portabil. Cum arată acum? Am încercat să păstrez răspunsul simplu. Am menționat că poate fi necesar să booleanizați cu !!x. Dacă acesta a fost propriul meu răspuns, s-ar putea să inserez mai mult text despre folosirea întotdeauna nesemnată, dar doar păstrez acest vechi răspuns canonic. (Jeremy, sper că vă plac modificările, s-ar putea să doriți să editați pentru a le pune modificările în propriile cuvinte sau orice altceva doriți să spuneți aproape 9 ani mai târziu.)
- person Peter Cordes; 11.11.2017
nal-lea în x a fost adăugată de o altă editare terță parte și nu a fost opera lui Jeremy în primul rând.
- person Peter Cordes; 11.11.2017
set bit to value. În general, fac save state + unconditionally set/clear + restore state în hardware. Oricum, am dat întâmplător de asta și cu siguranță voi fura expresia de mai sus.
- person sherrellbc; 15.01.2021
number = (number | (1UL << n)) ^ (!x << n) Simplificat pentru a elimina un nu logic și un nu pe biți
- person Shogan Aversa-Druesne; 18.01.2021
x. Nu ar genera number = (number & ~(1UL << n)) | (x << n); deplasări de doi biți, un flip de un octet, unul AND, unul SAU și, în sfârșit, o atribuire. În timp ce if (x) { number |= (1 << n); } else { number &= ~(1 << n); } ar genera o comparație, o deplasare de biți, fie un SAU sau un AND cu o schimbare de octet, precum și o atribuire. 6 vs 5 operațiuni în cel mai rău caz (ștergerea bitului). 6 vs 4 la setarea bitului. Mult mai lizibil. Dar poate că unele operațiuni sunt mai costisitoare?
- person Smartskaft2; 02.05.2021
x alternativ și nealternant, cu toate acestea, atunci când se distribuie aleatoriu valoarea lui x, soluția non-if este de două ori mai rapidă decât soluția if: godbolt.org/z/5aEbKchzf Deci, da, ai dreptate, predicția ramurilor distruge performanța.
- person JulianH; 28.05.2021
Folosind biblioteca standard C++: std::bitset<N>.
Sau versiunea Boost: boost::dynamic_bitset.
Nu este nevoie să vă rotiți singur:
#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
Versiunea Boost permite un set de biți de dimensiunea de rulare în comparație cu o bibliotecă standard de dimensiunea de compilare set de biți.
sudo apt-get install boost-devel
- person Martin York; 13.08.2014
std::bitset doar aici.
- person Martin York; 22.01.2020
Cealaltă opțiune este să utilizați câmpuri de biți:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
definește un câmp de 3 biți (de fapt, sunt trei câmpuri de 1 biți). Operațiile cu biți devin acum puțin (haha) mai simple:
Pentru a seta sau a șterge puțin:
mybits.b = 1;
mybits.c = 0;
Pentru a comuta puțin:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Verificand putin:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Acest lucru funcționează numai cu câmpuri de biți de dimensiune fixă. În caz contrar, trebuie să recurgeți la tehnicile de biți-twiddling descrise în postările anterioare.
sizeof(mybits), primesc 12 (adică dimensiunea de trei ints). Acesta este spațiul alocat în memorie sau vreo eroare în funcția sizeof?
- person iGbanam; 29.11.2012
gcc versiunea 4.4.5
- person iGbanam; 29.11.2012
struct poate fi introdus într-un union (de obicei anonim) cu un număr întreg etc. Funcționează. (Îmi dau seama că acesta este un thread vechi, btw)
- person Shade; 17.05.2014
__attribute__((packed))
- person ; 28.06.2020
Folosesc macrocomenzi definite într-un fișier antet pentru a gestiona setarea și ștergerea biților:
/* 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)))
#define BITMASK_CHECK_ANY(x,y) ((x) & (y))
BITMASK_CHECK(x,y) ((x) & (y)) trebuie să fie ((x) & (y)) == (y), altfel returnează un rezultat incorect pe mască multibiți (ex. 5 vs. 3) /*Bună ziua tuturor groparilor :)*/
- person brigadir; 11.12.2014
1 ar trebui să fie (uintmax_t)1 sau similar în cazul în care cineva încearcă să folosească aceste macrocomenzi pe un tip long sau mai mare
- person M.M; 07.02.2015
1ULL funcționează la fel de bine ca (uintmax_t) pe majoritatea implementărilor.
- person Peter Cordes; 11.11.2017
BITMASK_CHECK_ALL(x,y) poate fi implementat ca !~((~(y))|(x))
- person Handy999; 20.11.2018
!(~(x) & (y))
- person Tavian Barnes; 13.08.2019
#define BIT_CHECK(a,b) (!!((a) & (1ULL<<(b)))) // '!!' to make sure this returns 0 or 1
- person William Martens; 16.02.2021
Uneori merită să folosiți un enum pentru a numi biții:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Apoi folosiți numele mai târziu. i.e. scrie
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
pentru a seta, a clarifica și a testa. În acest fel, ascundeți numerele magice de restul codului.
În afară de asta, susțin soluția lui Jeremy.
clearbits() în loc de &= ~. De ce folosești o enumerare pentru asta? Am crezut că acestea sunt pentru a crea o grămadă de variabile unice cu valoare arbitrară ascunsă, dar le atribuiți fiecăreia o valoare definită. Deci, care este beneficiul față de doar definirea lor ca variabile?
- person endolith; 20.12.2011
enums pentru seturi de constante înrudite merge înapoi cu mult în programarea c. Bănuiesc că, cu compilatoarele moderne, singurul avantaj față de const short sau orice altceva este că sunt grupate în mod explicit împreună. Și când le doriți pentru ceva altul decât măștile de biți, obțineți numerotarea automată. În c++, desigur, ele formează și tipuri distincte, ceea ce vă oferă un mic plus de verificare statică a erorilor.
- person dmckee --- ex-moderator kitten; 22.12.2011
enum ThingFlags pentru ThingError|ThingFlag1, de exemplu?
- person Luis Colorado; 30.09.2014
int. Acest lucru poate cauza tot felul de erori subtile din cauza promovării întregi implicite sau a operațiunilor pe biți pe tipurile semnate. thingstate = ThingFlag1 >> 1 va invoca, de exemplu, comportamentul definit de implementare. thingstate = (ThingFlag1 >> x) << y poate invoca un comportament nedefinit. Și așa mai departe. Pentru a fi în siguranță, aruncați întotdeauna un tip nesemnat.
- person Lundin; 14.12.2015
enum My16Bits: unsigned short { ... };
- person Aiken Drum; 15.03.2016
Din snip-c.zip bitops.h:
/*
** 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)))
Bine, hai să analizăm lucrurile...
Expresia comună cu care se pare că aveți probleme în toate acestea este „(1L ‹‹ (posn))”. Tot ceea ce face este să creeze o mască cu un singur bit activat și care va funcționa cu orice tip de întreg. Argumentul „posn” specifică poziția în care doriți bitul. Dacă posn==0, atunci această expresie va fi evaluată la:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Dacă posn==8, se va evalua:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Cu alte cuvinte, pur și simplu creează un câmp de 0 cu un 1 la poziția specificată. Singura parte dificilă este în macrocomanda BitClr() unde trebuie să setăm un singur bit 0 într-un câmp de 1. Acest lucru se realizează prin utilizarea complementului 1 al aceleiași expresii notate de operatorul tilde (~).
Odată ce masca este creată, se aplică argumentului așa cum sugerați, prin utilizarea operatorilor biți și (&), sau (|) și xor (^). Deoarece masca este de tip lung, macrocomenzile vor funcționa la fel de bine pe caractere, scurte, int sau lungi.
Concluzia este că aceasta este o soluție generală pentru o întreagă clasă de probleme. Desigur, este posibil și chiar adecvat să rescrieți echivalentul oricăreia dintre aceste macrocomenzi cu valori explicite ale măștii de fiecare dată când aveți nevoie de una, dar de ce o faceți? Amintiți-vă, substituția macro are loc în preprocesor și astfel codul generat va reflecta faptul că valorile sunt considerate constante de către compilator - adică este la fel de eficient să utilizați macrocomenzile generalizate ca să „reinventați roata” de fiecare dată când trebuie să faceți. faceți manipularea biților.
Neconvins? Iată un cod de test - am folosit Watcom C cu optimizare completă și fără a folosi _cdecl, astfel încât dezasamblarea rezultată să fie cât mai curată posibil:
----[ 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 (dezasamblat) ]-------------------------------------- ---------
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 este long long. 1L trebuie să fie cel mai larg tip posibil, deci (uintmax_t)1 . (S-ar putea să scapi cu 1ull)
- person M.M; 07.02.2015
Pentru începători aș dori să explic puțin mai multe cu un exemplu:
Exemplu:
value is 0x55;
bitnum : 3rd.
Operatorul & este utilizat verificați bitul:
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)
Comutați sau răsturnați:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
operator |: setați bitul
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Deoarece acesta este etichetat „încorporat”, voi presupune că utilizați un microcontroler. Toate sugestiile de mai sus sunt valide și funcționează (citire-modificare-scriere, uniuni, structuri etc.).
Cu toate acestea, în timpul unei crize de depanare bazată pe osciloscop, am fost uimit să descopăr că aceste metode au o supraîncărcare considerabilă în ciclurile CPU, în comparație cu scrierea unei valori direct în registrele PORTnSET / PORTnCLEAR ale micro-ului, ceea ce face o diferență reală acolo unde există bucle strânse / ridicate. - pini de comutare ISR de frecventa.
Pentru cei nefamiliarizați: În exemplul meu, micro are un registru general de stare a pinului PORTn care reflectă pinii de ieșire, astfel încât PORTn |= BIT_TO_SET are ca rezultat o citire-modificare-scriere în acel registru. Cu toate acestea, registrele PORTnSET / PORTnCLEAR iau un „1” pentru a însemna „vă rugăm să faceți acest bit 1” (SET) sau „vă rugăm să faceți acest bit zero” (CLEAR) și un „0” pentru a însemna „lasați pinul în pace”. deci, ajungeți cu două adrese de porturi, în funcție de dacă setați sau ștergeți bitul (nu întotdeauna convenabil), dar o reacție mult mai rapidă și un cod asamblat mai mic.
volatile și, prin urmare, compilatorul nu poate efectua nicio optimizare a codului care implică astfel de registre. Prin urmare, este o practică bună să dezasamblați un astfel de cod și să vedeți cum a ieșit la nivel de asamblare.
- person Lundin; 14.12.2015
Iată macrocomanda mea aritmetică de biți preferată, care funcționează pentru orice tip de matrice de numere întregi fără semn de la unsigned char până la size_t (care este cel mai mare tip cu care ar trebui să funcționeze eficient):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Pentru a seta un pic:
BITOP(array, bit, |=);
Pentru a clarifica un pic:
BITOP(array, bit, &=~);
Pentru a comuta puțin:
BITOP(array, bit, ^=);
Pentru a testa un pic:
if (BITOP(array, bit, &)) ...
etc.
BITOP(array, bit++, |=); într-o buclă, cel mai probabil, nu va face ceea ce dorește apelantul.
- person foraidt; 13.07.2010
BITCELL(a,b) |= BITMASK(a,b); (ambele iau a ca argument pentru a determina dimensiunea, dar acesta din urmă nu ar fi niciodată evaluați a deoarece apare doar în sizeof).
- person R.. GitHub STOP HELPING ICE; 13.07.2010
(size_t) pare să fie acolo doar pentru a asigura unele matematice nesemnate cu %. Ar putea (unsigned) acolo.
- person chux - Reinstate Monica; 27.09.2017
(size_t)(b)/(8*sizeof *(a)) s-ar putea îngusta în mod inutil b înainte de diviziune. Doar o problemă cu matricele de biți foarte mari. Încă un macro interesant.
- person chux - Reinstate Monica; 27.09.2017
Abordarea bitfield are alte avantaje în arena încorporată. Puteți defini o structură care se mapează direct pe biții dintr-un anumit registru 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;
Trebuie să fiți conștienți de ordinea de ambalare a biților - cred că este mai întâi MSB, dar acest lucru poate depinde de implementare. De asemenea, verificați modul în care compilatorul dvs. gestionează câmpurile care traversează granițele octeților.
Apoi puteți citi, scrie, testa valorile individuale ca înainte.
Verificați puțin într-o locație arbitrară într-o variabilă de tip arbitrar:
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Eșantion de utilizare:
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: acesta este conceput pentru a fi rapid (dată fiind flexibilitatea sa) și fără ramuri. Rezultă un cod de mașină SPARC eficient atunci când este compilat Sun Studio 8; De asemenea, l-am testat folosind MSVC++ 2008 pe amd64. Este posibil să faci macrocomenzi similare pentru setarea și ștergerea biților. Diferența cheie a acestei soluții în comparație cu multe altele de aici este că funcționează pentru orice locație în aproape orice tip de variabilă.
Mai general, pentru hărți de bit de dimensiuni arbitrare:
#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 este deja definit de limits.h, nu trebuie să introduceți propriul BITS (și, de fapt, vă înrăutățiți codul făcând asta)
- person M.M; 07.02.2015
Acest program este de a schimba orice bit de date de la 0 la 1 sau de la 1 la 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);
}
}
}
Foloseste asta:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Dacă faci o mulțime de zbârcituri, s-ar putea să vrei să folosești măști care vor face totul mai rapid. Următoarele funcții sunt foarte rapide și sunt încă flexibile (permit răsucirea biților în hărți de biți de orice dimensiune).
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;
}
Rețineți, pentru a seta bitul „n” într-un număr întreg de 16 biți, faceți următoarele:
TSetBit( n, &my_int);
Depinde de dvs. să vă asigurați că numărul de biți se află în intervalul hărții de biți pe care o treceți. Rețineți că, pentru procesoarele Little Endian care octeți, cuvinte, dwords, qwords etc., se mapează corect între ele în memorie (motivul principal pentru care procesoarele Little Endian sunt „mai bune” decât procesoarele Big-Endian, ah, simt că vine un război de flacără pe...).
Extinderea răspunsului bitset:
#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;
}
Să presupunem mai întâi câteva lucruri
num = 55Întregul pentru a efectua operații pe biți (setați, obțineți, ștergeți, comutați).n = 4Poziția de biți bazată pe 0 pentru a efectua operațiuni pe biți.
Cum să obții un pic?
- Pentru a obține
nthbit de num shift dreaptanum,nori. Apoi executați ȘI&pe biți cu 1.
bit = (num >> n) & 1;
Cum funcționează?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
Cum se stabilesc un pic?
- Pentru a seta un anumit bit de număr. Schimbarea la stânga de 1
nori. Apoi efectuați operația SAU|pe biți cunum.
num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Cum funcționează?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
Cum se curata putin?
- Schimbarea la stânga 1, de
nori, adică1 << n. - Efectuați complement pe biți cu rezultatul de mai sus. Astfel încât al n-lea bit devine dezactivat și restul bitului devine setat, adică
~ (1 << n). - În cele din urmă, efectuați operația AND
&pe biți cu rezultatul de mai sus șinum. Cei trei pași de mai sus împreună pot fi scrisi canum & (~ (1 << n));
num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Cum funcționează?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Cum să comuți puțin?
Pentru a comuta puțin, folosim operatorul XOR ^ pe biți. Operatorul XOR pe biți evaluează la 1 dacă bitul corespunzător al ambilor operanzi este diferit, în caz contrar evaluează la 0.
Ceea ce înseamnă să comutăm puțin, trebuie să efectuăm operația XOR cu bitul pe care doriți să îl comutați și 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Cum funcționează?
- Dacă bitul de comutat este 0, atunci
0 ^ 1 => 1. - Dacă bitul de comutat este 1, atunci
1 ^ 1 => 0.
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
^ 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Lectură recomandată - Exerciții pentru operatori pe biți
Visual C 2010 și, probabil, multe alte compilatoare, au suport direct pentru operațiile booleene încorporate. Un bit are două valori posibile, la fel ca un boolean, așa că putem folosi boolean-uri în schimb - chiar dacă ocupă mai mult spațiu decât un singur bit în memorie în această reprezentare. Acest lucru funcționează, chiar și operatorul sizeof() funcționează corect.
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);
}
Așadar, la întrebarea dvs., IsGph[i] =1 sau IsGph[i] =0 facilitează setarea și ștergerea boolurilor.
Pentru a găsi caractere neimprimabile:
// 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;
}
}
Rețineți că acest cod nu are nimic „special”. Se tratează puțin ca un număr întreg - ceea ce, din punct de vedere tehnic, este. Un întreg de 1 bit care poate conține 2 valori și numai 2 valori.
Am folosit odată această abordare pentru a găsi înregistrări duplicate de împrumut, unde loan_number era cheia ISAM, folosind numărul de împrumut din 6 cifre ca index în matricea de biți. Frumos de rapid și, după 8 luni, a dovedit că sistemul mainframe de la care primim datele funcționa de fapt defectuos. Simplitatea matricelor de biți face ca încrederea în corectitudinea lor să fie foarte mare - față de o abordare de căutare, de exemplu.
bool. Poate chiar 4 octeți pentru setările C89 care folosesc int pentru a implementa bool
- person M.M; 07.02.2015
Dacă doriți să efectuați această operațiune cu programarea C în kernel-ul Linux, vă sugerez să utilizați API-urile standard ale nucleului Linux.
Consultați 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
Notă: Aici întreaga operațiune are loc într-un singur pas. Deci, toate acestea sunt garantate a fi atomice chiar și pe computerele SMP și sunt utile pentru a menține coerența între procesoare.
Iată câteva macrocomenzi pe care le folosesc:
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)
Cum setați, ștergeți și comutați un singur bit?
Pentru a rezolva o capcană obișnuită de codare atunci când încercați să formați măști:1 nu este întotdeauna suficient de largă
Ce probleme se întâmplă când number este un tip mai larg decât 1?x poate fi prea mare pentru schimbarea 1 << x care duce la comportament nedefinit (UB). Chiar dacă x nu este prea mare, ~ s-ar putea să nu răstoarne suficienți biți cei mai semnificativi.
// 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
Pentru a vă asigura că 1 este suficient de lat:
Codul ar putea folosi 1ull sau pedant (uintmax_t)1 și să lase compilatorul să se optimizeze.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Sau difuzare - ceea ce face ca problemele de codificare/revizuire/întreținere să mențină distribuția corectă și actualizată.
number |= (type_of_number)1 << x;
Sau promovați ușor 1 forțând o operație matematică care este cel puțin la fel de largă ca tipul de number.
number |= (number*0 + 1) << x;
Ca și în cazul majorității manipulărilor de biți, este mai bine să lucrați cu tipuri nesemnate decât cu cele semnate
number |= (type_of_number)1 << x;, nici number |= (number*0 + 1) << x; nu sunt potrivite pentru a seta bitul de semn al unui tip cu semn... De fapt, nici number |= (1ull << x); nu este. Există o modalitate portabilă de a face acest lucru în funcție de poziție?
- person chqrlie; 28.09.2017
Acest program se bazează pe soluția de mai sus a lui @Jeremy. Dacă cineva dorește să se joace rapid.
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
O versiune șablon (introdusă într-un fișier antet) cu suport pentru schimbarea mai multor biți (funcționează pe microcontrolere AVR de altfel):
namespace bit {
template <typename T1, typename T2>
constexpr inline T1 bitmask(T2 bit)
{return (T1)1 << bit;}
template <typename T1, typename T3, typename ...T2>
constexpr inline T1 bitmask(T3 bit, T2 ...bits)
{return ((T1)1 << bit) | bitmask<T1>(bits...);}
/** Set these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void set (T1 &variable, T2 ...bits)
{variable |= bitmask<T1>(bits...);}
/** Set only these bits (others will be cleared) */
template <typename T1, typename ...T2>
constexpr inline void setOnly (T1 &variable, T2 ...bits)
{variable = bitmask<T1>(bits...);}
/** Clear these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void clear (T1 &variable, T2 ...bits)
{variable &= ~bitmask<T1>(bits...);}
/** Flip these bits (others retain their state) */
template <typename T1, typename ...T2>
constexpr inline void flip (T1 &variable, T2 ...bits)
{variable ^= bitmask<T1>(bits...);}
/** Check if any of these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isAnySet(const T1 &variable, T2 ...bits)
{return variable & bitmask<T1>(bits...);}
/** Check if all these bits are set */
template <typename T1, typename ...T2>
constexpr inline bool isSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) == bitmask<T1>(bits...));}
/** Check if all these bits are not set */
template <typename T1, typename ...T2>
constexpr inline bool isNotSet (const T1 &variable, T2 ...bits)
{return ((variable & bitmask<T1>(bits...)) != bitmask<T1>(bits...));}
}
Exemplu de utilizare:
#include <iostream>
#include <bitset> // for console output of binary values
// and include the code above of course
using namespace std;
int main() {
uint8_t v = 0b1111'1100;
bit::set(v, 0);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,1);
cout << bitset<8>(v) << endl;
bit::clear(v, 0,1,2,3,4,5,6,7);
cout << bitset<8>(v) << endl;
bit::flip(v, 0,7);
cout << bitset<8>(v) << endl;
}
BTW: Se dovedește că constexpr și inline nu sunt folosite dacă nu se trimit argumentul de optimizare (de exemplu: -O3) la compilator. Simțiți-vă liber să încercați codul la https://godbolt.org/ și să priviți rezultatul ASM.
; după definițiile funcției dvs.?)
- person melpomene; 10.02.2018
((variable & bits) == bits)
- person Joakim L. Christiansen; 27.02.2018
std::bitsetîn c++11
- person pqnet; 25.10.2019
Setarea celui de-al n-lea bit la x (valoarea bitului) fără a utiliza -1
Uneori, când nu sunteți sigur la ce va rezulta -1 sau ceva asemănător, poate doriți să setați al n-lea bit fără a utiliza -1:
number = (((number | (1 << n)) ^ (1 << n))) | (x << n);
Explicație: ((number | (1 << n) setează al n-lea bit la 1 (unde | indică SAU pe biți), apoi cu (...) ^ (1 << n) setăm al n-lea bit la 0, iar în final cu (...) | x << n) setăm al n-lea bit care a fost 0, la (valoarea biților) x.
Acest lucru funcționează și în golang.
(number & ~(1 << n)) | (!!x << n).
- person Will Eccles; 08.05.2021
Iată o rutină în C pentru a efectua operațiunile de bază pe biți:
#define INT_BIT (unsigned int) (sizeof(unsigned int) * 8U) //number of bits in unsigned int
int main(void)
{
unsigned int k = 5; //k is the bit position; here it is the 5th bit from the LSb (0th bit)
unsigned int regA = 0x00007C7C; //we perform bitwise operations on regA
regA |= (1U << k); //Set kth bit
regA &= ~(1U << k); //Clear kth bit
regA ^= (1U << k); //Toggle kth bit
regA = (regA << k) | regA >> (INT_BIT - k); //Rotate left by k bits
regA = (regA >> k) | regA << (INT_BIT - k); //Rotate right by k bits
return 0;
}
Încercați una dintre aceste funcții în limbajul C pentru a schimba n biți:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Or
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Or
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 poate provoca un comportament nedefinit
- person M.M; 07.02.2015
1 în 0x1 sau 1UL pentru a evita UB despre care vorbește @M.M
- person KPCT; 18.04.2021
check_nth_bit poate fi bool.
- person Xeverous; 05.05.2020
