Jak ustawić, wyczyścić i przełączyć nieco?
Jak ustawić, wyczyścić i przełączyć nieco?
Odpowiedzi:
Użyj bitowego operatora OR ( |
), aby ustawić bit.
number |= 1UL << n;
To ustawi n
th th number
. n
powinno wynosić zero, jeśli chcesz ustawić 1
bit st i tak dalej n-1
, jeśli chcesz ustawić n
bit.
Użyj 1ULL
jeśli number
jest szerszy niż unsigned long
; promocja 1UL << n
nie następuje dopiero po sprawdzeniu, 1UL << n
gdzie zachowanie jest niezdefiniowane, aby przesunąć się o więcej niż szerokość a long
. To samo dotyczy wszystkich pozostałych przykładów.
Użyj bitowego operatora AND ( &
), aby trochę wyczyścić.
number &= ~(1UL << n);
To wyczyści n
trochę number
. Musisz odwrócić ciąg bitów za pomocą bitowego operatora NOT ( ~
), a następnie AND.
Za pomocą operatora XOR ( ^
) można nieco przełączyć.
number ^= 1UL << n;
To przełączy n
trochę number
.
Nie prosiłeś o to, ale równie dobrze mogę to dodać.
Aby to sprawdzić, przesuń liczbę n w prawo, a następnie bitowo ORAZ:
bit = (number >> n) & 1U;
Spowoduje to wstawienie wartości n
tego bitu number
do zmiennej bit
.
Ustawienie n
tego bitu na jeden 1
lub jeden 0
może być osiągnięte za pomocą następujących elementów w implementacji C ++ dla komplementu 2:
number ^= (-x ^ number) & (1UL << n);
Bit n
zostanie ustawiony, jeśli x
jest 1
, i wyczyszczony, jeśli x
jest 0
. Jeśli x
ma jakąś inną wartość, dostajesz śmieci. x = !!x
wstawi wartość booleanize na 0 lub 1.
Aby uniezależnić to od zachowania negacji dopełniacza 2 (gdzie -1
ustawiono wszystkie bity, w przeciwieństwie do implementacji dopełniacza 1 lub implementacji C ++ znaku / wielkości), użyj negacji bez znaku.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
lub
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Zasadniczo dobrym pomysłem jest używanie typów niepodpisanych do przenośnej manipulacji bitami.
lub
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
wyczyści n
bit th i (x << n)
ustawi n
th th na x
.
Dobrym pomysłem jest również, aby nie kopiować / wklejać kodu, więc wiele osób korzysta z makr preprocesora (np . Odpowiedzi społeczności wiki na dole ) lub jakiegoś rodzaju enkapsulacji.
bit = (number >> x) & 1
1
jest int
literałem, który jest podpisany. Więc wszystkie operacje tutaj działają na podpisanych numerach, co nie jest dobrze zdefiniowane przez standardy. Standardy nie gwarantują uzupełnienia dwóch lub przesunięcia arytmetycznego, więc lepiej jest użyć 1U
.
number = number & ~(1 << n) | (x << n);
zmienić n-ty bit na x.
Korzystanie z standardowa biblioteka C ++: std::bitset<N>
.
Lub doładowania wersja: boost::dynamic_bitset
.
Nie musisz rzucać własnym:
#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
Wersja Boost umożliwia zestaw bitów w czasie wykonywania w porównaniu ze standardowym zestawem bitów w czasie kompilacji biblioteki .
Inną opcją jest użycie pól bitowych:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
definiuje pole 3-bitowe (w rzeczywistości są to trzy 1-bitowe pola). Operacje bitowe stają się teraz nieco (haha) prostsze:
Aby ustawić lub wyczyścić nieco:
mybits.b = 1;
mybits.c = 0;
Aby nieco przełączyć:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Trochę sprawdzam:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Działa to tylko z polami bitowymi o stałej wielkości. W przeciwnym razie musisz uciekać się do technik kruszenia bitów opisanych w poprzednich postach.
Używam makr zdefiniowanych w pliku nagłówkowym do obsługi zestawu bitów i czyszczenia:
/* 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))
musi być ((x) & (y)) == (y)
inaczej zwróci niepoprawny wynik na masce wielobitowej (np. 5
vs. 3
) / * Witam wszystkich grabarzy
1
powinien być (uintmax_t)1
podobny w przypadku, gdy ktoś spróbuje użyć tych makr na long
typie większym lub większym
BITMASK_CHECK_ALL(x,y)
można zaimplementować jako!~((~(y))|(x))
!(~(x) & (y))
Czasami warto użyć enum
do nazwania bitów:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Następnie użyj tych nazw później. To znaczy pisz
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
ustawić, wyczyścić i przetestować. W ten sposób ukrywasz magiczne liczby przed resztą kodu.
Poza tym popieram rozwiązanie Jeremy'ego.
clearbits()
funkcję zamiast &= ~
. Dlaczego używasz do tego wyliczenia? Myślałem, że służą one do tworzenia wiązki unikalnych zmiennych z ukrytą dowolną wartością, ale przypisujesz każdemu z nich określoną wartość. Jaka jest więc korzyść w porównaniu do definiowania ich jako zmiennych?
enum
s do zestawów stałych pokrewnych ma długą tradycję w programowaniu c. Podejrzewam, że dzięki nowoczesnym kompilatorom jedyną przewagą nad nimi const short
jest to, że są one wyraźnie zgrupowane. A kiedy chcesz ich do czegoś innego niż maski bitowe, otrzymujesz automatyczną numerację. Oczywiście w c ++ tworzą one również różne typy, co daje trochę dodatkowych statycznych kontroli błędów.
enum ThingFlags
wartość ThingError|ThingFlag1
, na przykład?
int
. Może to powodować różnego rodzaju subtelne błędy z powodu niejawnej promocji liczb całkowitych lub operacji bitowych na podpisanych typach. thingstate = ThingFlag1 >> 1
wywoła na przykład zachowanie zdefiniowane w implementacji. thingstate = (ThingFlag1 >> x) << y
może wywoływać niezdefiniowane zachowanie. I tak dalej. Aby być bezpiecznym, zawsze przesyłaj do niepodpisanego typu.
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, przeanalizujmy rzeczy ...
Powszechnym wyrażeniem, z którym wydaje się, że masz problemy we wszystkich tych przypadkach, jest „(1L << (posn))”. Wszystko to polega na utworzeniu maski z jednym bitem, która będzie działać z dowolnym typem całkowitym. Argument „posn” określa pozycję, w której chcesz bit. Jeśli posn == 0, to wyrażenie będzie miało wartość:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Jeśli posn == 8, oceni, że:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Innymi słowy, po prostu tworzy pole zer z 1 w określonej pozycji. Jedyną trudną częścią jest makro BitClr (), w którym musimy ustawić pojedynczy bit 0 w polu 1. Dokonuje się tego za pomocą dopełniacza 1 tego samego wyrażenia, co oznaczono przez operator tyldy (~).
Po utworzeniu maski jest ona stosowana do argumentu tak, jak sugerujesz, za pomocą operatorów bitowych i (&) lub (|) i xor (^). Ponieważ maska jest długa, makra będą działać równie dobrze na char, short, int lub long.
Najważniejsze jest to, że jest to ogólne rozwiązanie całej klasy problemów. Oczywiście jest możliwe, a nawet właściwe przepisać odpowiednik któregokolwiek z tych makr za pomocą jawnych wartości maski za każdym razem, gdy jest to potrzebne, ale dlaczego? Pamiętaj, że zastępowanie makr zachodzi w preprocesorze, więc wygenerowany kod będzie odzwierciedlał fakt, że kompilator uważa, że wartości są stałe - tzn. Użycie makr uogólnionych jest równie skuteczne, jak „ponowne wymyślenie koła” za każdym razem, gdy trzeba zrobić trochę manipulacji.
Nie przekonany? Oto kod testowy - użyłem Watcom C z pełną optymalizacją i bez użycia _cdecl, aby wynikowy demontaż był jak najbardziej czysty:
---- [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 (zdemontowane)] -------------------------------------- ---------
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
jest long long
. 1L
musi być najszerszym możliwym typem, więc (uintmax_t)1
. (Możesz uciec 1ull
)
Użyj operatorów bitowych: &
|
Aby ustawić ostatni bit w 000b
:
foo = foo | 001b
Aby sprawdzić ostatni bit w foo
:
if ( foo & 001b ) ....
Aby wyczyścić ostatni bit w foo
:
foo = foo & 110b
Użyłem XXXb
dla jasności. Prawdopodobnie będziesz pracować z reprezentacją HEX, w zależności od struktury danych, w której pakujesz bity.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Dla początkujących chciałbym wyjaśnić nieco więcej przykładem:
Przykład:
value is 0x55;
bitnum : 3rd.
&
Operator służy sprawdzeniu bitu:
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)
Przełącz lub odwróć:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
operator: ustaw bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Oto moje ulubione bitowe makro arytmetyczne, które działa na każdym typie tablicy liczb całkowitych bez znaku od unsigned char
do size_t
(który jest największym typem, który powinien być wydajny w pracy):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Aby ustawić trochę:
BITOP(array, bit, |=);
Aby trochę wyczyścić:
BITOP(array, bit, &=~);
Aby nieco przełączyć:
BITOP(array, bit, ^=);
Aby trochę przetestować:
if (BITOP(array, bit, &)) ...
itp.
BITOP(array, bit++, |=);
z pętli najprawdopodobniej nie spełni oczekiwań osoby dzwoniącej.
BITCELL(a,b) |= BITMASK(a,b);
(zarówno odbioru a
jako argument do określenia wielkości, ale ten nigdy nie oceniać a
, ponieważ pojawia się tylko w sizeof
).
(size_t)
obsada wydaje się być nie tylko, aby zapewnić pewne niepodpisane matematyki z %
. Może (unsigned)
tam
(size_t)(b)/(8*sizeof *(a))
Niepotrzebnie może zmniejszyć b
przed podziałem. Tylko problem z bardzo dużymi tablicami bitów. Wciąż ciekawe makro.
Ponieważ jest to oznaczone jako „osadzone”, założę, że używasz mikrokontrolera. Wszystkie powyższe sugestie są prawidłowe i działają (odczyt-modyfikacja-zapis, związki, struktury itp.).
Jednak podczas serii debugowania opartego na oscyloskopie byłem zaskoczony, gdy zauważyłem, że metody te mają znaczny narzut w cyklach procesora w porównaniu z zapisywaniem wartości bezpośrednio w rejestrach PORTnSET / PORTnCLEAR mikroprocesora, co stanowi istotną różnicę w przypadku ciasnych pętli / wysokich Częstotliwość przełączania pinów ISR.
Dla tych, którzy nie są zaznajomieni: W moim przykładzie mikro ma ogólny rejestr stanu PORTn stanu pinów, który odzwierciedla styki wyjściowe, więc wykonanie PORTn | = BIT_TO_SET powoduje odczyt i modyfikację zapisu w tym rejestrze. Jednak rejestry PORTnSET / PORTnCLEAR przyjmują „1”, co oznacza „proszę zrobić ten bit 1” (SET) lub „proszę zrobić ten bit zero” (CLEAR) i „0”, co oznacza „zostawić pin w spokoju”. tak więc otrzymujesz dwa adresy portów w zależności od tego, czy ustawiasz, czy kasujesz bit (nie zawsze jest to wygodne), ale znacznie szybszą reakcję i mniejszy złożony kod.
volatile
a zatem kompilator nie jest w stanie wykonać żadnych optymalizacji kodu obejmującego takie rejestry. Dlatego dobrą praktyką jest dezasemblowanie takiego kodu i sprawdzanie, jak wyszło na poziomie asemblera.
Podejście do pola bitowego ma inne zalety na wbudowanej arenie. Możesz zdefiniować strukturę, która będzie mapowana bezpośrednio na bity w konkretnym rejestrze sprzętowym.
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;
Musisz być świadomy kolejności pakowania bitów - myślę, że to najpierw MSB, ale może to zależeć od implementacji. Sprawdź także, w jaki sposób procedury obsługi kompilatora przekraczają granice bajtów.
Następnie możesz czytać, pisać, testować poszczególne wartości jak poprzednio.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Przykładowe użycie:
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;
}
Uwagi: Został zaprojektowany jako szybki (biorąc pod uwagę jego elastyczność) i nierozgałęziony. Skutkuje to wydajnym kodem maszynowym SPARC podczas kompilacji Sun Studio 8; Przetestowałem to również przy użyciu MSVC ++ 2008 na amd64. Możliwe jest tworzenie podobnych makr do ustawiania i czyszczenia bitów. Kluczową różnicą tego rozwiązania w porównaniu z wieloma innymi tutaj jest to, że działa on dla dowolnej lokalizacji w prawie każdym typie zmiennej.
Ogólniej, w przypadku bitmap o dowolnej wielkości:
#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
jest już zdefiniowane przez limits.h
, nie musisz wstawiać własnego BITS
(a tak naprawdę pogarszasz swój kod)
Ten program służy do zmiany dowolnego bitu danych z 0 na 1 lub 1 na 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);
}
}
}
Jeśli robisz dużo kręcenia, możesz chcieć użyć masek, które przyspieszą. Poniższe funkcje są bardzo szybkie i wciąż elastyczne (pozwalają na kręcenie bitów w mapach bitowych o dowolnym rozmiarze).
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;
}
Uwaga: aby ustawić bit „n” w 16-bitowej liczbie całkowitej, wykonaj następujące czynności:
TSetBit( n, &my_int);
Od Ciebie zależy, czy liczba bitów będzie w zakresie przekazywanej mapy bitów. Zwróć uwagę, że w przypadku małych procesorów endian, które bajty, słowa, dwory, qwords itp. Poprawnie odwzorowują się w pamięci (główny powód, dla którego małe procesory endianowe są „lepsze” niż procesory big-endian, ach, czuję, że nadchodzi wojna płomieni na...).
Użyj tego:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Rozwijając bitset
odpowiedź:
#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;
}
Jeśli chcesz wykonać tę całą operację z programowaniem C w jądrze Linuksa , sugeruję użycie standardowych interfejsów API jądra Linux.
Zobacz 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
Uwaga: Tutaj cała operacja odbywa się w jednym kroku. Gwarantuje to, że są one atomowe nawet na komputerach SMP i są przydatne do zachowania spójności między procesorami.
Visual C 2010 i być może wiele innych kompilatorów ma wbudowane bezpośrednie wsparcie dla operacji boolowskich. Trochę ma dwie możliwe wartości, podobnie jak boolean, więc zamiast tego możemy używać boolanów - nawet jeśli zajmują więcej miejsca niż jeden bit w pamięć w tej reprezentacji. To działa, nawet sizeof()
operator działa poprawnie.
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);
}
Więc na twoje pytanie IsGph[i] =1
, lubIsGph[i] =0
ułatw sobie ustawianie i usuwanie booli.
Aby znaleźć niedrukowalne znaki:
// 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;
}
}
Uwaga: w tym kodzie nie ma nic „specjalnego”. Traktuje to trochę jak liczbę całkowitą - co technicznie jest. 1-bitowa liczba całkowita, która może pomieścić 2 wartości i tylko 2 wartości.
Kiedyś użyłem tego podejścia, aby znaleźć duplikaty rekordów pożyczek, gdzie numer_ pożyczki był kluczem ISAM, używając 6-cyfrowego numeru pożyczki jako indeksu w tablicy bitów. Niesamowicie szybki i po 8 miesiącach udowodnił, że system mainframe, z którego otrzymywaliśmy dane, w rzeczywistości działał nieprawidłowo. Prostota tablic bitowych sprawia, że pewność ich poprawności jest bardzo wysoka - na przykład w stosunku do wyszukiwania.
bool
. Może nawet 4 bajty dla konfiguracji C89, które używają int
do implementacjibool
Oto kilka makr, których używam:
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)
Zastosowana zmienna
int value, pos;
wartość -
Pozycja danych - pozycja bitu, którą chcemy ustawić, wyczyścić lub przełączyć.
Ustaw trochę:
value = value | 1 << pos;
Wyczyść trochę:
value = value & ~(1 << pos);
Przełącz trochę:
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
może być bool
.
Załóżmy, że najpierw kilka
num = 55
liczb całkowitych wykonuje operacje bitowe (ustaw, pobierz, wyczyść, przełącz).
n = 4
0 bitowa pozycja do wykonywania operacji bitowych.
nth
odrobinę num prawej zmiany num
, n
razy. Następnie wykonaj bitowe AND &
z 1.bit = (num >> n) & 1;
Jak to działa?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
razy. Następnie wykonaj bitową operację LUB za |
pomocą num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Jak to działa?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
razy tj 1 << n
.~ (1 << n)
.&
z powyższym wynikiem i num
. Powyższe trzy kroki razem można zapisać jako num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Jak to działa?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Aby nieco przełączyć, używamy bitowego XOR ^
operatora . Bitowy operator XOR ocenia na 1, jeśli odpowiadający mu bit obu argumentów jest inny, w przeciwnym razie ocenia na 0.
Co oznacza, że należy trochę przełączyć, musimy wykonać operację XOR z bitem, który chcesz przełączyć i 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Jak to działa?
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)
Zalecana lektura - Bitowe ćwiczenia operatora
Jak ustawić, wyczyścić i przełączyć jeden bit?
Aby rozwiązać problem typowego pułapki kodowania podczas próby utworzenia maski:
1
nie zawsze jest wystarczająco szeroki
Jakie problemy się zdarzają, gdy number
jest szerszy niż 1
?
x
może być zbyt wielki dla zmiany 1 << x
prowadzącej do niezdefiniowanego zachowania (UB). Nawet jeśli x
nie jest zbyt wielki, ~
może nie odwrócić wystarczającej liczby najbardziej znaczących bitów.
// 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
Aby zapewnić, że 1 jest wystarczająco szeroki:
Kod może użyć 1ull
lub pedantycznie (uintmax_t)1
i pozwolić kompilatorowi zoptymalizować.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Lub obsada - co powoduje problemy z kodowaniem / przeglądem / konserwacją, dzięki czemu obsada jest poprawna i aktualna.
number |= (type_of_number)1 << x;
Lub delikatnie promuj 1
poprzez wymuszanie operacji matematycznej, która jest co najmniej tak szeroka jak rodzaj number
.
number |= (number*0 + 1) << x;
Jak w większości bitowe manipulacji, najlepiej do pracy z niepodpisanych typów zamiast podpisanych te
number |= (type_of_number)1 << x;
nie number |= (number*0 + 1) << x;
należy ustawiać bitu znaku typu podpisanego ... Właściwie nie jest number |= (1ull << x);
. Czy istnieje przenośny sposób na zrobienie tego według pozycji?
Wersja szablonowa C ++ 11 (umieszczona w nagłówku):
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;}
}
;
definicje funkcji?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
w c ++ 11
Ten program jest oparty na powyższym rozwiązaniu @ Jeremy. Jeśli ktoś chce szybko się pobawić.
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
Wypróbuj jedną z tych funkcji w języku C, aby zmienić bit n:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Lub
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Lub
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
może powodować niezdefiniowane zachowanie