Як встановити, очистити та змінити трохи?
Як встановити, очистити та змінити трохи?
Відповіді:
Використовуйте побітовий оператор АБО ( |
), щоб встановити трохи.
number |= 1UL << n;
Це встановить цей n
біт number
. n
має бути нульовим, якщо ви хочете встановити 1
st біт і так далі upto n-1
, якщо ви хочете встановитиn
th біт.
Використовувати, 1ULL
якщо number
ширше unsigned long
; просування по службі 1UL << n
не відбувається до того моменту, як оцінити, 1UL << n
де невизначена поведінка зміститься на більшу, ніж на ширинуlong
. Те саме стосується всіх інших прикладів.
Використовуйте побітне оператор AND ( &
), щоб трохи очистити.
number &= ~(1UL << n);
Це очистить цей n
шматочок number
. Ви повинні перевернути бітову рядок за допомогою оператора NOT ( ~
), а потім AND.
Оператор XOR ( ^
) може використовуватися для перемикання.
number ^= 1UL << n;
Це переключить цей n
біт number
.
Ви цього не просили, але я можу також додати його.
Щоб трохи перевірити, змістіть число n вправо, потім порозрядно І це:
bit = (number >> n) & 1U;
Це додасть значення n
th біта number
в зміннуbit
.
Встановлення n
th-біту або в будь-який, 1
або за його 0
допомогою можна досягти за допомогою наступного в додатку 2-го додатка C ++:
number ^= (-x ^ number) & (1UL << n);
Біт n
буде встановлений, якщо x
є 1
, і очищений, якщо x
є 0
. Якщо x
має якесь інше значення, ви отримуєте сміття. x = !!x
Буолеанізує його до 0 або 1.
Щоб зробити це незалежним від поведінки заперечення комплементу 2 (де -1
встановлено всі біти, на відміну від реалізації доповнення 1 або знака / величини C ++), використовуйте непідписане заперечення.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
або
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Зазвичай це гарна ідея використовувати неподписані типи для маніпуляцій з переносними бітами.
або
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
очистить n
біт і (x << n)
встановить цей n
біт наx
.
Загалом, це гарна ідея взагалі не копіювати / вставляти код, і тому багато людей використовують макроси попереднього процесу (наприклад, відповідь на вікі спільноти далі ) або якусь інкапсуляцію.
bit = (number >> x) & 1
1
є int
буквальним, яке підписується. Тож усі операції тут діють за підписаними номерами, що недостатньо визначено стандартами. Стандарти не гарантують доповнення двох або арифметичні зміни, тому їх краще використовувати 1U
.
number = number & ~(1 << n) | (x << n);
для зміни n-го біта на x.
Використання стандартної бібліотеки C ++: std::bitset<N>
.
Або версія Boost :boost::dynamic_bitset
.
Не потрібно прокручувати свої власні:
#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
Версія Boost дозволяє виконувати розмір біт-версії для виконання в порівнянні зі стандартним біт-бітом компіляції за часом.
Інший варіант - використовувати бітові поля:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
визначає 3-бітове поле (насправді це три 1-бітні поля). Бітові операції тепер стають трохи (ха-ха) простішими:
Щоб встановити чи очистити трохи:
mybits.b = 1;
mybits.c = 0;
Щоб трохи переключити:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Перевірка:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Це працює лише з бітовими полями фіксованого розміру. Інакше вам доведеться вдатися до технік біт-подвійності, описаних у попередніх публікаціях.
Я використовую макроси, визначені у файлі заголовка, для обробки встановлених бітів та очищення:
/* 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))
повинно бути ((x) & (y)) == (y)
інакше, воно повертає неправильний результат на багатобітній масці (наприклад, 5
проти 3
) / * Привіт всім могильникам:) * /
1
має бути (uintmax_t)1
або подібним, якщо хтось намагається використовувати ці макроси типу "" long
чи "" більшого розміру
BITMASK_CHECK_ALL(x,y)
можна реалізувати як!~((~(y))|(x))
!(~(x) & (y))
Іноді варто використовуючи enum
для імені біт:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Потім використовуйте імена згодом. Тобто пишуть
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
встановити, очистити і перевірити. Таким чином ви приховуєте магічні числа від решти коду.
Крім цього, я схвалюю рішення Джеремі.
clearbits()
функцію замість &= ~
. Чому для цього ви використовуєте енту? Я думав, що вони створюють купу унікальних змінних із прихованим довільним значенням, але ви присвоюєте певне значення кожному. То яка користь проти просто визначення їх як змінних?
enum
s для наборів пов'язаних констант далекий шлях у програмуванні c. Я підозрюю, що у сучасних компіляторів єдина перевага перед const short
тим, що вони явно згруповані. І коли ви хочете, щоб вони були чимось іншим, ніж бітмаски, ви отримуєте автоматичну нумерацію. Зрозуміло, що в c ++ вони також утворюють різні типи, що дає вам трохи додаткової статичної перевірки помилок.
enum ThingFlags
значення ThingError|ThingFlag1
, наприклад?
int
. Це може викликати всілякі помилки помилок через неявне ціле просування або побітові операції з підписаними типами. thingstate = ThingFlag1 >> 1
Наприклад, буде посилатися на певну поведінку. thingstate = (ThingFlag1 >> x) << y
може викликати невизначене поведінку. І так далі. Щоб забезпечити безпеку, завжди надайте непідписаний тип.
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)))
Гаразд, давайте проаналізуємо речі ...
Поширений вираз, з яким у вас, здається, виникають проблеми у всьому цьому, є "(1L << (posn))". Все це - це створити маску з одним бітом і, яка буде працювати з будь-яким цілим типом. Аргумент "posn" вказує позицію, де ви хочете біт. Якщо posn == 0, то цей вираз буде оцінено так:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Якщо posn == 8, він оцінить так:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Іншими словами, він просто створює поле 0 з позначенням 1 у вказаній позиції. Єдина складна частина - в макросі BitClr (), де нам потрібно встановити один 0 біт у полі 1. Це досягається за допомогою доповнення 1 тим самим виразом, що позначається оператором tilde (~).
Як тільки маска буде створена, вона застосовується до аргументу так само, як ви пропонуєте, використовуючи побітові та (&), або (|) та xor (^) оператори. Оскільки маска має тип довгих, макроси будуть працювати так само добре, як у char, short, int або long's.
Суть полягає в тому, що це загальне рішення цілого класу проблем. Звичайно, можна і навіть доречно переписувати еквівалент будь-якого з цих макросів із явними значеннями маски кожного разу, коли вам потрібен, але навіщо це робити? Пам'ятайте, що макрозаміщення відбувається в препроцесорі, і тому сформований код відображатиме той факт, що значення компілятором вважаються постійними, тобто використовувати так само настільки ефективно, як використовувати узагальнені макроси, щоб "винаходити колесо" кожного разу, коли потрібно робити бітові маніпуляції.
Непереконаний? Ось кілька тестових кодів - я використовував Watcom C з повною оптимізацією та без використання _cdecl, щоб отримана розбирання була максимально чистою:
---- [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 (розібраний)] -------------------------------------- ---------
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
---- [фініс] ------------------------------------------- ----------------------
arg
є long long
. 1L
має бути якомога ширшим типом, тому (uintmax_t)1
. (Ви можете піти з цього місця 1ull
)
Використовуйте побітові оператори: &
|
Щоб встановити останній біт у 000b
:
foo = foo | 001b
Щоб перевірити останній біт у foo
:
if ( foo & 001b ) ....
Щоб очистити останній біт у foo
:
foo = foo & 110b
Я використовував XXXb
для наочності. Ви, ймовірно, будете працювати з представленням HEX, залежно від структури даних, в яку ви пакуєте біти.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Для початківців я хотів би трохи пояснити приклад:
Приклад:
value is 0x55;
bitnum : 3rd.
&
Використовується оператор перевірити біт:
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)
Увімкнути або перевернути:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
оператор: встановити біт
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Ось мій улюблений арифметичний біт-макрос, який працює для будь-якого типу безпідписаного цілого масиву від unsigned char
до size_t
(що є найбільшим типом, з яким слід ефективно працювати):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Щоб встановити трохи:
BITOP(array, bit, |=);
Щоб трохи очистити:
BITOP(array, bit, &=~);
Щоб трохи переключити:
BITOP(array, bit, ^=);
Щоб трохи перевірити:
if (BITOP(array, bit, &)) ...
тощо.
BITOP(array, bit++, |=);
в циклі, швидше за все, не зробить те, що хоче абонент.
BITCELL(a,b) |= BITMASK(a,b);
(обидва беруть a
за аргумент для визначення розміру, але останній ніколи не оцінить, a
оскільки вона з’являється лише в sizeof
).
(size_t)
кидок , здається, є тільки забезпечити деяку беззнакову математику з %
. Може (unsigned)
там.
(size_t)(b)/(8*sizeof *(a))
Непотрібно може бути звужена b
до поділу. Тільки проблема з дуже великими масивами бітів. Ще цікавий макрос.
Оскільки це позначено "вбудованим", я вважаю, що ви використовуєте мікроконтролер. Усі вищезазначені пропозиції є дійсними та працюють (читати-змінювати-писати, спілки, структури тощо).
Однак під час налагодження налагодження на основі осцилоскопа я був здивований, виявивши, що ці методи мають значні накладні витрати в циклах процесора порівняно з записом значення безпосередньо в регістри PORTnSET / PORTnCLEAR мікро, що робить реальну різницю там, де є тісні петлі / високі -часті шпильки ISR.
Для незнайомих: У моєму прикладі мікро має загальний регістр PIN-стану PORTn, який відображає вихідні штифти, тому PORTn | = BIT_TO_SET призводить до зчитування-модифікації-запису до цього реєстру. Однак регістри PORTnSET / PORTnCLEAR приймають значення "1", яке означає "будь ласка, зробіть цей біт 1" (SET) або "будь ласка, зробіть цей біт нульовим" (CLEAR), а "0" означає "залишити шпильку в спокої". Таким чином, ви отримуєте дві адреси портів, залежно від того, встановлюєте ви чи очищаєте біт (не завжди зручно), але набагато швидша реакція та менший зібраний код.
volatile
і тому компілятор не може виконати жодних оптимізацій щодо коду, що включає такі регістри. Тому добре розбирати такий код і подивитися, як це вийшло на рівні асемблера.
Підхід бітфілда має й інші переваги на вбудованій арені. Ви можете визначити структуру, яка відображає безпосередньо біти в певному апаратному реєстрі.
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;
Вам потрібно знати про порядок упаковки бітів - я думаю, що це спочатку MSB, але це може залежати від реалізації. Також перевірте, як ваші обробники компіляторів перетинають межі байтів.
Потім ви можете читати, писати, перевіряти окремі значення, як і раніше.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Використання зразка:
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;
}
Примітки. Це розроблено так, щоб він був швидким (враховуючи його гнучкість) і не розгалужувався. Це призводить до ефективного машинного коду SPARC при компіляції Sun Studio 8; Я також тестував його, використовуючи MSVC ++ 2008 на amd64. Можна зробити подібні макроси для налаштування та очищення бітів. Ключова відмінність цього рішення порівняно з багатьма іншими тут полягає в тому, що воно працює для будь-якого місця в майже будь-якому типі змінної.
Більш загальне для растрових зображень будь-якого розміру:
#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
це вже визначено limits.h
, вам не потрібно вводити свої власні BITS
(а насправді ви робите свій код ще гіршим)
Ця програма повинна змінити будь-який біт даних від 0 до 1 або 1 до 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);
}
}
}
Якщо ви робите багато подруг, можливо, захочете використовувати маски, які зроблять все швидше. Наступні функції дуже швидкі і все ще гнучкі (вони дозволяють скручувати біт у бітових картах будь-якого розміру).
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;
}
Зауважте, щоб встановити біт 'n' у 16-бітовому цілому, виконайте наступне:
TSetBit( n, &my_int);
Вам належить переконатися, що число бітів знаходиться в межах діапазону бітової карти, яку ви передаєте. Зауважте, що для маленьких ендіанських процесорів, які байти, слова, dwords, qwords тощо, правильно відображати один одного в пам’яті (головна причина, що маленькі ендіанські процесори «кращі», ніж процесори з великим ендіаном, ах, я відчуваю, що наближається полум'яна війна на ...).
Використовуй це:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Розгортання 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;
}
Якщо ви хочете виконати цю операцію з програмуванням на C в ядрі Linux, то я пропоную використовувати стандартні API ядра Linux.
Дивіться 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
Примітка. Тут вся операція відбувається за один крок. Таким чином, всі вони гарантовано є атомними навіть на SMP-комп'ютерах і корисні для збереження узгодженості між процесорами.
Visual C 2010, і, можливо, багато інших компіляторів, мають пряму підтримку вбудованих операцій boolean. Трохи є два можливі значення, як булеві, тому ми можемо використовувати booleans замість цього, навіть якщо вони займають більше місця, ніж один біт у пам'ять у цьому поданні. Це працює, навіть sizeof()
оператор працює належним чином.
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);
}
Отже, до вашого питання, IsGph[i] =1
або IsGph[i] =0
полегшити налаштування та очищення булінів.
Щоб знайти недруковані символи:
// 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;
}
}
Зверніть увагу, що в цьому коді немає нічого особливого. Це трактує трохи як ціле число - що технічно це є. Ціле 1 біт, яке може містити лише 2 значення та 2 значення.
Я колись використовував такий підхід для пошуку дублікатів записів позики, де number_number був ключем ISAM, використовуючи 6-значний номер позики як індекс у бітовому масиві. Швидко, і через 8 місяців довів, що система мейнфреймів, з якої ми отримували дані, насправді несправна. Простота бітових масивів робить дуже впевненим у їх коректності - наприклад, підходом до пошуку.
bool
. Можливо, навіть 4 байти для налаштувань C89, які використовуються int
для реалізаціїbool
Ось кілька макросів, які я використовую:
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)
Використовувана змінна
int value, pos;
value -
Позиція даних - положення біта, яке нам цікаво встановити, очистити або переключити.
Встановіть трохи:
value = value | 1 << pos;
Почистіть трохи:
value = value & ~(1 << pos);
Трохи перемкніть:
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
може бути bool
.
Нехай спочатку кілька речей
num = 55
Integer виконує побітові операції (встановити, дістати, очистити, переключити).
n = 4
0 бітове положення для виконання побітових операцій.
nth
біт нумерації правильного зсуву num
, n
раз. Потім виконайте порозрядно І &
з 1.bit = (num >> n) & 1;
Як це працює?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
раз. Потім виконайте |
операцію бітового АБО за допомогою num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Як це працює?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
раз, тобто1 << n
.~ (1 << n)
.&
операцію з вищевказаним результатом і num
. Зазначені вище три кроки можна записати як num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Як це працює?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Для трохи перемикання ми використовуємо побітовий XOR ^
оператор . Побітовий оператор XOR оцінює до 1, якщо відповідні біти обох операндів різні, в іншому випадку - 0.
Що означає трохи перемикати, нам потрібно виконати операцію XOR з бітом, який ви хочете переключити, і 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Як це працює?
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)
Рекомендоване читання - бітові вправи оператора
Як встановити, очистити та переключити один біт?
Щоб вирішити загальну проблему кодування під час спроби сформувати маску:
1
не завжди достатньо широка
Які проблеми трапляються, коли number
ширший тип, ніж 1
?
x
може бути занадто великим для зрушення, що 1 << x
призводить до невизначеної поведінки (UB). Навіть якщо x
він не надто великий, він ~
може не перевернути достатньо значущих бітів.
// 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
Для страхування 1 досить широко:
Код можна використовувати 1ull
або педантично, (uintmax_t)1
і дозволити компілятору оптимізувати.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Або приведення - це робить проблемою з кодуванням / оглядом / технічним обслуговуванням, підтримуючи правильний і сучасний формат.
number |= (type_of_number)1 << x;
Або обережно рекламуйте 1
, змушуючи математичну операцію, яка є настільки ж широкою, як і тип number
.
number |= (number*0 + 1) << x;
Як і в більшості бітових маніпуляцій, найкраще працювати з беззнаковими типами , а не підписаний тими
number |= (type_of_number)1 << x;
ні number |= (number*0 + 1) << x;
доречно встановлювати бітовий знак підписаного типу ... Власне кажучи, і ні number |= (1ull << x);
. Чи є портативний спосіб зробити це за позицією?
Версія з шаблоном C ++ 11 (поміщена в заголовок):
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;}
}
;
після визначення своїх функцій?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
в c ++ 11
Ця програма заснована на вищезгаданому рішенні @ Джеремі. Якщо хтось хоче швидко пограти.
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
Спробуйте одну з цих функцій мовою C, щоб змінити n біт:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Або
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Або
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
може спричинити невизначеність поведінки