Como você define, limpa e alterna um pouco?
Como você define, limpa e alterna um pouco?
Respostas:
Use o operador OR bit a bit ( |
) para definir um pouco.
number |= 1UL << n;
Isso definirá o n
th bit de number
. n
deve ser zero, se você deseja definir o 1
st bit e assim por diante n-1
, se deseja definir o n
th bit.
Use 1ULL
se number
for maior que unsigned long
; a promoção de 1UL << n
não acontece até depois de avaliar 1UL << n
onde o comportamento indefinido muda mais do que a largura de umlong
. O mesmo se aplica a todo o restante dos exemplos.
Use o operador AND bit a bit ( &
) para limpar um pouco.
number &= ~(1UL << n);
Isso limpará o n
th th de number
. Você deve inverter a string de bits com o operador NOT bit a bit (~
) e depois AND.
O operador XOR ( ^
) pode ser usado para alternar um pouco.
number ^= 1UL << n;
Isso alternará o n
th bit denumber
.
Você não pediu isso, mas eu poderia adicioná-lo.
Para verificar um pouco, desloque o número n para a direita e, em seguida, bit a bit E:
bit = (number >> n) & 1U;
Isso colocará o valor do n
th bit number
na variávelbit
.
Definir o n
th bit como um 1
ou 0
pode ser alcançado com o seguinte na implementação C ++ de um complemento de 2:
number ^= (-x ^ number) & (1UL << n);
O bit n
será definido se x
estiver 1
e limpo se x
estiver 0
. Se x
tiver algum outro valor, você recebe lixo. x = !!x
o booleanizará para 0 ou 1.
Para tornar isso independente do comportamento de negação do complemento de 2 (onde -1
todos os bits foram definidos, diferentemente do complemento de 1 ou da implementação de sinal / magnitude C ++), use negação não assinada.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
ou
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
Geralmente, é uma boa ideia usar tipos não assinados para manipulação portátil de bits.
ou
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
irá limpar o n
th bit e (x << n)
definirá o n
th bit parax
.
Também é geralmente uma boa idéia não copiar / colar código em geral e muitas pessoas usam macros de pré-processador (como a resposta do wiki da comunidade mais adiante ) ou algum tipo de encapsulamento.
bit = (number >> x) & 1
1
é um int
literal, que é assinado. Portanto, todas as operações aqui operam com números assinados, o que não é bem definido pelos padrões. Os padrões não garantem o complemento de dois ou a mudança aritmética, portanto é melhor usá-lo 1U
.
number = number & ~(1 << n) | (x << n);
Alterar o n-ésimo bit para x.
Usando a biblioteca C ++ padrão: std::bitset<N>
.
Ou a versão Boost :boost::dynamic_bitset
.
Não há necessidade de criar o seu próprio:
#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
A versão Boost permite um conjunto de bits do tamanho de tempo de execução em comparação com um conjunto de bits do tamanho de compilação da biblioteca padrão .
A outra opção é usar campos de bits:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
define um campo de 3 bits (na verdade, são três campos de 1 bit). As operações de bit agora se tornam um pouco (haha) mais simples:
Para definir ou limpar um pouco:
mybits.b = 1;
mybits.c = 0;
Para alternar um pouco:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Verificando um pouco:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Isso funciona apenas com campos de bits de tamanho fixo. Caso contrário, você precisará recorrer às técnicas de manipulação de bits descritas nas postagens anteriores.
Eu uso macros definidas em um arquivo de cabeçalho para manipular o conjunto de bits e limpar:
/* 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 ser ((x) & (y)) == (y)
de outra forma retorna resultado incorreto na máscara multibit (ex. 5
vs. 3
/ * Olá a todos os coveiros:) * /)
1
deve ser (uintmax_t)1
ou semelhantes no caso de alguém tentar usar essas macros em um long
ou tipo maior
BITMASK_CHECK_ALL(x,y)
pode ser implementado como #!~((~(y))|(x))
!(~(x) & (y))
Às vezes vale a pena usar um enum
para nomear os bits:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Em seguida, use os nomes posteriormente. Ou seja, escrever
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
para definir, limpar e testar. Dessa forma, você oculta os números mágicos do restante do seu código.
Fora isso, eu apoio a solução de Jeremy.
clearbits()
função em vez de &= ~
. Por que você está usando um enum para isso? Eu pensei que eram para criar um monte de variáveis únicas com valor arbitrário oculto, mas você está atribuindo um valor definido para cada uma. Então, qual é o benefício versus apenas defini-los como variáveis?
enum
s para conjuntos de constantes relacionadas remonta a um longo caminho na programação c. Eu suspeito que, com os compiladores modernos, a única vantagem sobre const short
ou o que quer que seja é que eles estão explicitamente agrupados. E quando você quer que eles para algo outro do que bitmasks você começa a numeração automática. No c ++, é claro, eles também formam tipos distintos, o que fornece uma verificação extra de erros estáticos.
enum ThingFlags
valor ThingError|ThingFlag1
, por exemplo?
int
. Isso pode causar todos os tipos de erros sutis devido à promoção de número inteiro implícito ou operações bit a bit em tipos assinados. thingstate = ThingFlag1 >> 1
por exemplo, chamará o comportamento definido pela implementação. thingstate = (ThingFlag1 >> x) << y
pode invocar um comportamento indefinido. E assim por diante. Para estar seguro, sempre faça a conversão para um tipo não assinado.
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, vamos analisar as coisas ...
A expressão comum com a qual você parece estar tendo problemas em tudo isso é "(1L << (posn))". Tudo isso é criar uma máscara com um único bit ativado e que funcionará com qualquer tipo de número inteiro. O argumento "posn" especifica a posição em que você deseja o bit. Se posn == 0, esta expressão será avaliada para:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Se posn == 8, ele avaliará como:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
Em outras palavras, ele simplesmente cria um campo de 0 com um 1 na posição especificada. A única parte complicada está na macro BitClr (), na qual precisamos definir um único 0 bit em um campo de 1's. Isso é feito usando o complemento 1 da mesma expressão, conforme indicado pelo operador til (~).
Depois que a máscara é criada, ela é aplicada ao argumento, como você sugere, usando os operadores bit a bit e (&), ou (|) e xor (^). Como a máscara é do tipo longa, as macros também funcionarão com caracteres char, short, int ou long.
A linha inferior é que esta é uma solução geral para toda uma classe de problemas. É claro que é possível e até apropriado reescrever o equivalente a qualquer uma dessas macros com valores explícitos de máscara toda vez que você precisar, mas por que fazer isso? Lembre-se de que a substituição da macro ocorre no pré-processador e, portanto, o código gerado refletirá o fato de que os valores são considerados constantes pelo compilador - ou seja, é tão eficiente usar as macros generalizadas quanto "reinventar a roda" toda vez que você precisar faça manipulação de bits.
Não convencido? Aqui está um código de teste - usei o Watcom C com otimização total e sem usar _cdecl para que a desmontagem resultante fosse a mais limpa possível:
---- [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 (desmontado)] -------------------------------------- ---------
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
for long long
. 1L
precisa ser o tipo mais amplo possível (uintmax_t)1
. (Você pode se safar 1ull
)
Use os operadores bit a bit: &
|
Para definir o último bit em 000b
:
foo = foo | 001b
Para verificar o último bit foo
:
if ( foo & 001b ) ....
Para limpar o último bit em foo
:
foo = foo & 110b
Eu costumava ter XXXb
clareza. Você provavelmente estará trabalhando com representação HEX, dependendo da estrutura de dados na qual está compactando bits.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Para o iniciante, gostaria de explicar um pouco mais com um exemplo:
Exemplo:
value is 0x55;
bitnum : 3rd.
O &
operador é usado, verifique o 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)
Alternar ou Inverter:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
operador: defina o bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Aqui está minha macro aritmética de bits favorita, que funciona para qualquer tipo de matriz inteira não assinada de unsigned char
até size_t
(que é o maior tipo que deve ser eficiente para trabalhar):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Para definir um pouco:
BITOP(array, bit, |=);
Para limpar um pouco:
BITOP(array, bit, &=~);
Para alternar um pouco:
BITOP(array, bit, ^=);
Para testar um pouco:
if (BITOP(array, bit, &)) ...
etc.
BITOP(array, bit++, |=);
em loop provavelmente não fará o que o chamador deseja.
BITCELL(a,b) |= BITMASK(a,b);
(ambos usam a
como argumento para determinar o tamanho, mas o último nunca avaliaria a
desde aparece apenas em sizeof
).
(size_t)
elenco parecem estar lá apenas para segurar alguns matemática não assinado com %
. Poderia (unsigned)
lá.
(size_t)(b)/(8*sizeof *(a))
desnecessariamente poderia diminuir b
antes da divisão. Apenas um problema com matrizes de bits muito grandes. Ainda uma macro interessante.
Como isso está marcado como "incorporado", assumirei que você está usando um microcontrolador. Todas as sugestões acima são válidas e funcionam (leitura-modificação-gravação, uniões, estruturas, etc.).
No entanto, durante um período de depuração baseada em osciloscópio, fiquei surpreso ao descobrir que esses métodos têm uma sobrecarga considerável nos ciclos da CPU em comparação com a gravação de um valor diretamente nos registros PORTnSET / PORTnCLEAR do micro, o que faz uma diferença real quando existem loops estreitos / altos pinos de alternância do ISR de alta frequência.
Para quem não conhece: No meu exemplo, o micro possui um registro geral de estado de pino PORTn, que reflete os pinos de saída, e PORTn | = BIT_TO_SET resulta em uma leitura-modificação-gravação no registro. No entanto, os registradores PORTnSET / PORTnCLEAR levam um '1' para significar "por favor, faça este bit 1" (SET) ou "por favor, faça este bit zero" (CLEAR) e um '0' para significar "deixe o pino em paz". portanto, você acaba com dois endereços de porta, dependendo de definir ou limpar o bit (nem sempre é conveniente), mas uma reação muito mais rápida e um código montado menor.
volatile
e, portanto, o compilador não pode executar nenhuma otimização no código que envolve esses registros. Portanto, é uma boa prática desmontar esse código e ver como ficou no nível do assembler.
A abordagem de campo de bits tem outras vantagens na arena incorporada. Você pode definir uma estrutura que mapeie diretamente os bits em um registro de hardware específico.
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;
Você precisa estar ciente da ordem de compactação de bits - acho que é o MSB primeiro, mas isso pode depender da implementação. Além disso, verifique como os campos de manipuladores do compilador cruzam os limites de bytes.
Você pode então ler, escrever, testar os valores individuais como antes.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Uso da amostra:
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;
}
Notas: Ele foi projetado para ser rápido (dada sua flexibilidade) e não ramificado. Isso resulta em um código de máquina SPARC eficiente quando compilado o Sun Studio 8; Também testei usando o MSVC ++ 2008 em amd64. É possível criar macros semelhantes para definir e limpar bits. A principal diferença desta solução em comparação com muitas outras aqui é que ela funciona para qualquer local em praticamente qualquer tipo de variável.
Mais geral, para bitmaps de tamanho arbitrário:
#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
já está definido por limits.h
, você não precisa colocar no seu próprio BITS
(e na verdade você piorar seu código ao fazê-lo)
Este programa deve alterar qualquer bit de dados de 0 para 1 ou 1 para 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 você está mexendo bastante, talvez queira usar máscaras, o que tornará tudo mais rápido. As funções a seguir são muito rápidas e ainda são flexíveis (elas permitem rodar bit em bit maps de qualquer tamanho).
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;
}
Observe que, para definir o bit 'n' em um número inteiro de 16 bits, faça o seguinte:
TSetBit( n, &my_int);
Cabe a você garantir que o número de bits esteja dentro do intervalo do mapa de bits que você passa. Observe que, para pequenos processadores endian, que bytes, palavras, dwords, qwords etc. são mapeados corretamente um para o outro na memória (principal motivo pelo qual os pequenos processadores endian são 'melhores' que os processadores big endian, ah, sinto uma guerra de chamas chegando em...).
Usa isto:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Expandindo a bitset
resposta:
#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 você deseja executar todas essas operações com programação C no kernel do Linux , sugiro usar APIs padrão do kernel do Linux.
Veja 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: Aqui toda a operação acontece em uma única etapa. Portanto, todos eles são garantidos como atômicos, mesmo em computadores SMP, e são úteis para manter a coerência entre os processadores.
O Visual C 2010, e talvez muitos outros compiladores, têm suporte direto para operações booleanas incorporadas. Um bit tem dois valores possíveis, assim como um booleano, para que possamos usar booleanos - mesmo que ocupem mais espaço do que um único bit. memória nesta representação. Isso funciona, até o sizeof()
operador funciona corretamente.
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);
}
Portanto, para sua pergunta, IsGph[i] =1
ou IsGph[i] =0
facilite a configuração e limpeza de bools.
Para encontrar caracteres não imprimíveis:
// 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;
}
}
Observe que não há nada de "especial" nesse código. Trata-se um pouco como um número inteiro - o que tecnicamente é. Um número inteiro de 1 bit que pode conter 2 valores e apenas 2 valores.
Uma vez, usei essa abordagem para encontrar registros duplicados de empréstimos, em que loan_number era a chave ISAM, usando o número de empréstimo de 6 dígitos como índice na matriz de bits. Savagely rápido, e após 8 meses, provou que o sistema de mainframe do qual estávamos obtendo os dados estava de fato com defeito. A simplicidade das matrizes de bits torna a confiança em sua correção muito alta - em comparação com uma abordagem de pesquisa, por exemplo.
bool
. Talvez até 4 bytes para configurações C89 que usam int
para implementarbool
Aqui estão algumas macros que eu 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)
Variável usada
int value, pos;
value - Data
pos - posição do bit que estamos interessados em definir, limpar ou alternar.
Defina um pouco:
value = value | 1 << pos;
Limpe um pouco:
value = value & ~(1 << pos);
Alterne um pouco:
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
pode ser bool
.
Suponhamos que poucas coisas primeiro sejam
num = 55
Integer para executar operações bit a bit (definir, obter, limpar, alternar).
n = 4
Posição de bit com base em 0 para executar operações bit a bit.
nth
deslocamento certo num
, n
vezes. Em seguida, execute AND bit a bit &
com 1.bit = (num >> n) & 1;
Como funciona?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
vezes. Em seguida, execute a operação OR bit a bit |
com num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Como funciona?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
vezes ie 1 << n
.~ (1 << n)
.&
com o resultado acima e num
. Os três passos acima juntos podem ser escritos como num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Como funciona?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Para alternar um pouco, usamos o ^
operador XOR bit a bit . O operador XOR bit a bit avalia como 1 se o bit correspondente de ambos os operandos for diferente; caso contrário, avalia como 0.
O que significa alternar um pouco, precisamos executar a operação XOR com o bit que você deseja alternar e 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Como funciona?
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)
Leitura recomendada - Exercícios do operador Bitwise
Como você define, limpa e alterna um único bit?
Para resolver uma armadilha comum de codificação ao tentar formar a máscara:
1
nem sempre é grande o suficiente
Que problemas acontecem quando number
é do tipo mais amplo 1
?
x
pode ser muito grande para a mudança que 1 << x
leva ao comportamento indefinido (UB). Mesmo que x
não seja muito grande, ~
pode não ser suficiente para gerar bits mais significativos.
// 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
Para garantir que 1 seja amplo o suficiente:
O código pode ser usado de forma 1ull
pedanática (uintmax_t)1
e permitir que o compilador otimize.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Ou elenco - o que gera problemas de codificação / revisão / manutenção, mantendo o elenco correto e atualizado.
number |= (type_of_number)1 << x;
Ou promova gentilmente 1
forçando uma operação matemática que seja tão larga quanto o tipo de number
.
number |= (number*0 + 1) << x;
Como a maioria das manipulações de bits, melhor trabalhar com não assinados tipos em vez de assinados os
number |= (type_of_number)1 << x;
nem number |= (number*0 + 1) << x;
apropriadas para definir o bit de sinal de um tipo assinado ... Por uma questão de fato, nem é number |= (1ull << x);
. Existe uma maneira portátil de fazer isso por posição?
Uma versão do modelo C ++ 11 (colocada em um cabeçalho):
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;}
}
;
após suas definições de função?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
em c ++ 11
Este programa é baseado na solução acima do @ Jeremy. Se alguém quiser brincar 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
Tente uma destas funções na linguagem C para alterar n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
Ou
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
Ou
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
pode causar comportamento indefinido