¿Cómo establece, limpia y alterna un poco?
¿Cómo establece, limpia y alterna un poco?
Respuestas:
Use el operador OR a nivel de bit ( |
) para establecer un bit.
number |= 1UL << n;
Eso establecerá el n
th bit de number
. n
debe ser cero, si desea establecer el 1
bit st y así sucesivamente n-1
, si desea establecer el n
bit th.
Use 1ULL
if number
es más ancho que unsigned long
; la promoción de 1UL << n
no ocurre hasta después de evaluar 1UL << n
dónde es un comportamiento indefinido cambiar más de la anchura de a long
. Lo mismo se aplica a todos los demás ejemplos.
Use el operador AND a nivel de bit ( &
) para borrar un bit.
number &= ~(1UL << n);
Eso despejará la n
parte de number
. Debe invertir la cadena de bits con el operador NOT bit a bit ( ~
), luego Y.
El operador XOR ( ^
) se puede usar para alternar un poco.
number ^= 1UL << n;
Eso alternará la parte de n
th number
.
No pediste esto, pero también podría agregarlo.
Para verificar un bit, desplace el número n hacia la derecha, luego bit a bit Y:
bit = (number >> n) & 1U;
Eso pondrá el valor del n
th bit de number
en la variable bit
.
Establecer el n
bit th en cualquiera 1
o 0
se puede lograr con lo siguiente en una implementación C ++ del complemento a 2:
number ^= (-x ^ number) & (1UL << n);
El bit n
se establecerá si x
es 1
y se borrará si x
es así 0
. Si x
tiene algún otro valor, obtienes basura. x = !!x
lo booleanizará a 0 o 1.
Para que esto sea independiente del comportamiento de negación del complemento de 2 (donde -1
tiene todos los bits establecidos, a diferencia de un complemento de 1 o implementación de signo / magnitud C ++), use la negación sin signo.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
o
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
En general, es una buena idea usar tipos sin signo para la manipulación de bits portátil.
o
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
borrará el n
bit th y (x << n)
establecerá el n
bit th en x
.
También es generalmente una buena idea no copiar / pegar código en general, y muchas personas usan macros de preprocesador (como la respuesta de la wiki de la comunidad más abajo ) o algún tipo de encapsulación.
bit = (number >> x) & 1
1
es un int
literal, que está firmado. Entonces, todas las operaciones aquí operan con números firmados, que no están bien definidos por los estándares. Los estándares no garantizan el complemento de dos o el cambio aritmético, por lo que es mejor usarlo 1U
.
number = number & ~(1 << n) | (x << n);
cambiar el enésimo bit a x.
Uso de la biblioteca estándar de C ++: std::bitset<N>
.
O el Boost versión: boost::dynamic_bitset
.
No hay necesidad de rodar el suyo:
#include <bitset>
#include <iostream>
int main()
{
std::bitset<5> x;
x[1] = 1;
x[2] = 0;
// Note x[0-4] valid
std::cout << x << std::endl;
}
[Alpha:] > ./a.out
00010
La versión Boost permite un conjunto de bits de tamaño de tiempo de ejecución en comparación con un conjunto de bits de tamaño de compilación de biblioteca estándar .
La otra opción es usar campos de bits:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
define un campo de 3 bits (en realidad, son tres campos de 1 bit). Las operaciones de bit ahora se vuelven un poco (jaja) más simples:
Para establecer o borrar un poco:
mybits.b = 1;
mybits.c = 0;
Para alternar un poco:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Comprobando un poco:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Esto solo funciona con campos de bits de tamaño fijo. De lo contrario, debe recurrir a las técnicas de cambio de bits descritas en publicaciones anteriores.
Uso macros definidas en un archivo de encabezado para manejar el conjunto de bits y borrar:
/* 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))
debe ser de lo ((x) & (y)) == (y)
contrario, devuelve un resultado incorrecto en la máscara multibit (ej. 5
vs. 3
) / * Hola a todos los sepultureros
1
debería ser (uintmax_t)1
o similar en caso de que alguien intente usar estas macros en un long
tipo o más grande
BITMASK_CHECK_ALL(x,y)
se puede implementar como!~((~(y))|(x))
!(~(x) & (y))
A veces vale la pena usar un enum
para nombrar los bits:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Luego use los nombres más adelante. Es decir, escribir
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
para establecer, borrar y probar. De esta manera, oculta los números mágicos del resto de su código.
Aparte de eso, respaldo la solución de Jeremy.
clearbits()
función en lugar de &= ~
. ¿Por qué estás usando una enumeración para esto? Pensé que eran para crear un montón de variables únicas con un valor arbitrario oculto, pero estás asignando un valor definido a cada una. Entonces, ¿cuál es el beneficio en comparación con solo definirlos como variables?
enum
s para conjuntos de constantes relacionadas se remonta mucho en la programación de c. Sospecho que con los compiladores modernos, la única ventaja sobre, const short
o lo que sea, es que están agrupados explícitamente. Y cuando los quiere para algo más que máscaras de bits, obtiene la numeración automática. En c ++, por supuesto, también forman tipos distintos que le brindan un poco más de comprobación de errores estáticos.
enum ThingFlags
sirve ThingError|ThingFlag1
, por ejemplo?
int
. Esto puede causar todo tipo de errores sutiles debido a la promoción de números enteros implícitos u operaciones bit a bit en tipos firmados. thingstate = ThingFlag1 >> 1
invocará, por ejemplo, un comportamiento definido por la implementación. thingstate = (ThingFlag1 >> x) << y
puede invocar un comportamiento indefinido. Y así. Para estar seguro, utilice siempre un tipo sin signo.
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, analicemos las cosas ...
La expresión común con la que parece estar teniendo problemas en todo esto es "(1L << (posn))". Todo lo que hace es crear una máscara con un solo bit y que funcionará con cualquier tipo de entero. El argumento "posn" especifica la posición donde desea el bit. Si posn == 0, entonces esta expresión se evaluará para:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Si posn == 8, se evaluará para:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
En otras palabras, simplemente crea un campo de 0 con un 1 en la posición especificada. La única parte difícil es en la macro BitClr () donde necesitamos establecer un solo 0 bit en un campo de 1. Esto se logra utilizando el complemento de 1 de la misma expresión que denota el operador tilde (~).
Una vez que se crea la máscara, se aplica al argumento tal como lo sugiere, mediante el uso de los operadores bit a bit y (&), o (|) y xor (^). Como la máscara es de tipo long, las macros funcionarán igual de bien en char's, short's, int's o long's.
La conclusión es que esta es una solución general para toda una clase de problemas. Por supuesto, es posible e incluso apropiado reescribir el equivalente de cualquiera de estas macros con valores de máscara explícitos cada vez que lo necesite, pero ¿por qué hacerlo? Recuerde, la sustitución de macros ocurre en el preprocesador, por lo que el código generado reflejará el hecho de que el compilador considera que los valores son constantes, es decir, es tan eficiente usar las macros generalizadas como "reinventar la rueda" cada vez que necesite hacer manipulación de bits.
Poco convencido? Aquí hay un código de prueba: utilicé Watcom C con optimización completa y sin usar _cdecl, por lo que el desmontaje resultante sería lo más limpio posible:
---- [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
es así long long
. 1L
necesita ser el tipo más amplio posible, entonces (uintmax_t)1
. (Puede salirse con la suya 1ull
)
Use los operadores bit a bit: &
|
Para configurar el último bit en 000b
:
foo = foo | 001b
Para comprobar el último bit en foo
:
if ( foo & 001b ) ....
Para borrar el último bit en foo
:
foo = foo & 110b
Solía XXXb
por claridad. Probablemente trabajará con representación HEX, dependiendo de la estructura de datos en la que está empacando bits.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Para el principiante, me gustaría explicar un poco más con un ejemplo:
Ejemplo:
value is 0x55;
bitnum : 3rd.
El &
operador se utiliza comprobar el 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 o voltear:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
operador: establece el bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Aquí está mi macro aritmética de bits favorita, que funciona para cualquier tipo de matriz entera sin signo de unsigned char
hasta size_t
(que es el tipo más grande con el que debería ser eficiente trabajar):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Para establecer un poco:
BITOP(array, bit, |=);
Para despejar un poco:
BITOP(array, bit, &=~);
Para alternar un poco:
BITOP(array, bit, ^=);
Para probar un poco:
if (BITOP(array, bit, &)) ...
etc.
BITOP(array, bit++, |=);
en bucle probablemente no hará lo que la persona que llama desea.
BITCELL(a,b) |= BITMASK(a,b);
(ambos toman a
como argumento para determinar el tamaño, pero este último nunca se evaluaría a
ya que aparece solo en sizeof
).
(size_t)
elenco parece ser no sólo para asegurar cierta matemáticas sin firmar con %
. Podría (unsigned)
allí.
(size_t)(b)/(8*sizeof *(a))
innecesariamente podría reducirse b
antes de la división. Solo un problema con matrices de bits muy grandes. Sigue siendo una macro interesante.
Como esto está etiquetado como "incrustado", supondré que está utilizando un microcontrolador. Todas las sugerencias anteriores son válidas y funcionan (lectura-modificación-escritura, uniones, estructuras, etc.).
Sin embargo, durante una serie de depuración basada en el osciloscopio, me sorprendió descubrir que estos métodos tienen una sobrecarga considerable en los ciclos de la CPU en comparación con escribir un valor directamente en los registros PORTnSET / PORTnCLEAR del micro, lo que hace una diferencia real cuando hay bucles estrechos / altos -frecuencia ISR pasadores de alternancia.
Para aquellos que no están familiarizados: en mi ejemplo, el micro tiene un registro general de estado de pin PORTn que refleja los pines de salida, por lo que hacer PORTn | = BIT_TO_SET da como resultado una lectura-modificación-escritura en ese registro. Sin embargo, los registros PORTnSET / PORTnCLEAR toman un '1' para significar "por favor haga este bit 1" (SET) o "por favor haga que este bit sea cero" (CLEAR) y un '0' para significar "deje el pin en paz". entonces, terminas con dos direcciones de puerto dependiendo de si estás configurando o borrando el bit (no siempre es conveniente) pero una reacción mucho más rápida y un código ensamblado más pequeño.
volatile
y, por lo tanto, el compilador no puede realizar ninguna optimización en el código que involucra dichos registros. Por lo tanto, es una buena práctica desensamblar dicho código y ver cómo resultó a nivel de ensamblador.
El enfoque de campo de bits tiene otras ventajas en el ámbito integrado. Puede definir una estructura que se asigne directamente a los bits en un registro de hardware particular.
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;
Debe tener en cuenta el orden de empaquetado de bits: creo que es MSB primero, pero esto puede depender de la implementación. Además, verifique cómo los manejadores del compilador cruzan los límites de bytes.
Luego puede leer, escribir, probar los valores individuales como antes.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Uso de la muestra:
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: Esto está diseñado para ser rápido (dada su flexibilidad) y no ramificado. Da como resultado un código de máquina SPARC eficiente cuando se compila Sun Studio 8; También lo probé usando MSVC ++ 2008 en amd64. Es posible hacer macros similares para configurar y borrar bits. La diferencia clave de esta solución en comparación con muchas otras aquí es que funciona para cualquier ubicación en prácticamente cualquier tipo de variable.
Más general, para mapas de bits de tamaño arbitrario:
#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
ya está definido por limits.h
, no necesita poner el suyo BITS
(y de hecho empeora su código al hacerlo)
Este programa es para cambiar cualquier bit de datos de 0 a 1 o 1 a 0:
{
unsigned int data = 0x000000F0;
int bitpos = 4;
int bitvalue = 1;
unsigned int bit = data;
bit = (bit>>bitpos)&0x00000001;
int invbitvalue = 0x00000001&(~bitvalue);
printf("%x\n",bit);
if (bitvalue == 0)
{
if (bit == 0)
printf("%x\n", data);
else
{
data = (data^(invbitvalue<<bitpos));
printf("%x\n", data);
}
}
else
{
if (bit == 1)
printf("elseif %x\n", data);
else
{
data = (data|(bitvalue<<bitpos));
printf("else %x\n", data);
}
}
}
Si está haciendo muchos giros, es posible que desee usar máscaras que harán que todo sea más rápido. Las siguientes funciones son muy rápidas y siguen siendo flexibles (permiten girar bits en mapas de bits de cualquier tamaño).
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;
}
Tenga en cuenta que para establecer el bit 'n' en un entero de 16 bits, haga lo siguiente:
TSetBit( n, &my_int);
Depende de usted asegurarse de que el número de bits esté dentro del rango del mapa de bits que pasa. Tenga en cuenta que para los pequeños procesadores endian que bytes, palabras, dwords, qwords, etc., se asignan correctamente entre sí en la memoria (razón principal de que los pequeños procesadores endian son "mejores" que los procesadores big endian, ah, siento que se acerca una guerra de llamas en...).
Utilizar este:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Ampliando la bitset
respuesta:
#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;
}
Si desea realizar todas estas operaciones con la programación C en el kernel de Linux , le sugiero que use API estándar del kernel de Linux.
Ver 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: Aquí toda la operación ocurre en un solo paso. Por lo tanto, se garantiza que todos estos elementos serán atómicos incluso en computadoras SMP y son útiles para mantener la coherencia entre los procesadores.
Visual C 2010, y quizás muchos otros compiladores, tienen soporte directo para operaciones booleanas incorporadas. Un bit tiene dos valores posibles, al igual que un booleano, por lo que podemos usar booleanos en su lugar, incluso si ocupan más espacio que un solo bit en memoria en esta representación. Esto funciona, incluso el sizeof()
operador funciona correctamente.
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);
}
Por lo tanto, a su pregunta, IsGph[i] =1
o IsGph[i] =0
facilite la configuración y la limpieza de las carpetas.
Para buscar caracteres no imprimibles:
// 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;
}
}
Tenga en cuenta que no hay nada "especial" en este código. Se trata un poco como un número entero, que técnicamente lo es. Un entero de 1 bit que puede contener 2 valores y solo 2 valores.
Una vez utilicé este enfoque para encontrar registros de préstamos duplicados, donde el número de préstamo era la clave ISAM, usando el número de préstamo de 6 dígitos como un índice en la matriz de bits. Salvajemente rápido, y después de 8 meses, demostró que el sistema mainframe del que estábamos obteniendo los datos estaba funcionando mal. La simplicidad de las matrices de bits hace que la confianza en su corrección sea muy alta, en comparación con un enfoque de búsqueda, por ejemplo.
bool
. Quizás incluso 4 bytes para las configuraciones de C89 que se usan int
para implementarbool
Aquí hay algunas macros que 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)
Variable utilizada
int value, pos;
valor -
Posición de datos - posición del bit que estamos interesados en establecer, borrar o alternar.
Establecer un poco:
value = value | 1 << pos;
Claro un poco:
value = value & ~(1 << pos);
Alternar un poco:
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
puede ser bool
.
Supongamos que algunas cosas primero
num = 55
Integer realizar operaciones bit a bit (set, get, clear, toggle).
n = 4
Posición de bit basada en 0 para realizar operaciones bit a bit.
nth
bit de desplazamiento a la derecha num num
, n
tiempos. Luego realice bit a bit Y &
con 1.bit = (num >> n) & 1;
¿Cómo funciona?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
veces. Luego realice la |
operación OR a nivel de bit con num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
¿Cómo funciona?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
ej 1 << n
.~ (1 << n)
.&
operación AND a nivel de bit con el resultado anterior y num
. Los tres pasos anteriores juntos se pueden escribir como num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
¿Cómo 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 un poco, usamos el ^
operador XOR bit a bit . El operador XOR bit a bit evalúa a 1 si el bit correspondiente de ambos operandos es diferente, de lo contrario se evalúa a 0.
Lo que significa alternar un poco, necesitamos realizar la operación XOR con el bit que desea alternar y 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
¿Cómo 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)
Lectura recomendada: ejercicios de operador bit a bit
¿Cómo establece, limpia y alterna un solo bit?
Para abordar un error de codificación común al intentar formar la máscara:
1
no siempre es lo suficientemente ancho
¿Qué problemas ocurren cuando number
es un tipo más amplio que 1
?
x
puede ser demasiado bueno para el cambio que 1 << x
conduce a un comportamiento indefinido (UB). Incluso si x
no es demasiado bueno, ~
puede que no voltee suficientes bits más 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 asegurar 1 es lo suficientemente ancho:
El código podría usarse 1ull
o pedantemente (uintmax_t)1
y permitir que el compilador se optimice.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
O emitir, lo que hace que los problemas de codificación / revisión / mantenimiento mantengan el reparto correcto y actualizado.
number |= (type_of_number)1 << x;
O promueva suavemente 1
forzando una operación matemática que sea tan ancha como el tipo de number
.
number |= (number*0 + 1) << x;
Al igual que con la mayoría de las manipulaciones de bits, mejor trabajar con sin signo tipos en lugar de firmados los
number |= (type_of_number)1 << x;
ni number |= (number*0 + 1) << x;
apropiado para establecer el bit de signo de un tipo con signo ... De hecho, tampoco lo es number |= (1ull << x);
. ¿Hay alguna forma portátil de hacerlo por posición?
Una versión con plantilla de C ++ 11 (poner en un encabezado):
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;}
}
;
después de sus definiciones de función?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
en c ++ 11
Este programa se basa en la solución anterior de @ Jeremy. Si alguien desea jugar rápidamente.
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
Pruebe una de estas funciones en el lenguaje C para cambiar n bit:
char bitfield;
// Start at 0th position
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & (~( (1 << n) ^ (value << n) ));
}
O
void chang_n_bit(int n, int value)
{
bitfield = (bitfield | (1 << n)) & ((value << n) | ((~0) ^ (1 << n)));
}
O
void chang_n_bit(int n, int value)
{
if(value)
bitfield |= 1 << n;
else
bitfield &= ~0 ^ (1 << n);
}
char get_n_bit(int n)
{
return (bitfield & (1 << n)) ? 1 : 0;
}
value << n
puede causar un comportamiento indefinido