rotacion operadores mascaras mascara manipulacion manejo hacer ejercicios ejemplos desplazamiento con como algorithm bit-manipulation

algorithm - operadores - Algoritmo para generar máscara de bits



operadores de bits en c (6)

Me enfrentaba a este problema único de generar una máscara de bits basada en el parámetro de entrada. Por ejemplo,

si param = 2, entonces la máscara será 0x3 (11b) si param = 5, entonces la máscara será 0x1F (1 1111b)

Esto lo implementé usando un for-loop en C, algo así como

int nMask = 0; for (int i = 0; i < param; i ++) { nMask |= (1 << i); }

Me gustaría saber si hay un algoritmo mejor ~~~


Implementación eficiente, sin sucursales, portátil y genérica (pero fea)

DO:

#include <limits.h> /* CHAR_BIT */ #define BIT_MASK(__TYPE__, __ONE_COUNT__) / ((__TYPE__) (-((__ONE_COUNT__) != 0))) / & (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__)))

C ++:

#include <climits> template <typename R> static constexpr R bitmask(unsigned int const onecount) { // return (onecount != 0) // ? (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount)) // : 0; return static_cast<R>(-(onecount != 0)) & (static_cast<R>(-1) >> ((sizeof(R) * CHAR_BIT) - onecount)); }

Uso (producir constantes de tiempo de compilación)

BIT_MASK(unsigned int, 4) /* = 0x0000000f */ BIT_MASK(uint64_t, 26) /* = 0x0000000003ffffffULL */

Ejemplo

#include <stdio.h> int main() { unsigned int param; for (param = 0; param <= 32; ++param) { printf("%u => 0x%08x/n", param, BIT_MASK(unsigned int, param)); } return 0; }

Salida

0 => 0x00000000 1 => 0x00000001 2 => 0x00000003 3 => 0x00000007 4 => 0x0000000f 5 => 0x0000001f 6 => 0x0000003f 7 => 0x0000007f 8 => 0x000000ff 9 => 0x000001ff 10 => 0x000003ff 11 => 0x000007ff 12 => 0x00000fff 13 => 0x00001fff 14 => 0x00003fff 15 => 0x00007fff 16 => 0x0000ffff 17 => 0x0001ffff 18 => 0x0003ffff 19 => 0x0007ffff 20 => 0x000fffff 21 => 0x001fffff 22 => 0x003fffff 23 => 0x007fffff 24 => 0x00ffffff 25 => 0x01ffffff 26 => 0x03ffffff 27 => 0x07ffffff 28 => 0x0fffffff 29 => 0x1fffffff 30 => 0x3fffffff 31 => 0x7fffffff 32 => 0xffffffff

Explicación

En primer lugar, como ya se explicó en otras respuestas, se usa << en lugar de << para evitar el problema cuando el conteo de turnos es igual al número de bits del tipo de almacenamiento del valor. (Gracias a la respuesta de Julien por la idea)

Para facilitar la discusión, vamos a "crear una instancia" de la macro con unsigned int como __TYPE__ y ver qué sucede (asumiendo 32 bits por el momento):

((unsigned int) (-((__ONE_COUNT__) != 0))) / & (((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))

Centrémonos en:

((sizeof(unsigned int) * CHAR_BIT)

primero. Se conoce sizeof(unsigned int) en tiempo de compilación. Es igual a 4 según nuestro supuesto. CHAR_BIT representa el número de bits por char , también conocido por byte. También es conocido en tiempo de compilación. Es igual a 8 en la mayoría de las máquinas en la Tierra. Dado que esta expresión se conoce en tiempo de compilación, el compilador probablemente haría la multiplicación en tiempo de compilación y la trataría como una constante, lo que equivale a 32 en este caso.

Vayamos a:

((unsigned int) -1)

Es igual a 0xFFFFFFFF . La conversión de -1 a cualquier tipo sin signo produce un valor de "all-1s" en ese tipo. Esta parte es también una constante de tiempo de compilación.

Hasta ahora, la expresión:

(((unsigned int) -1) >> ((sizeof(unsigned int) * CHAR_BIT) - (__ONE_COUNT__)))

es de hecho lo mismo que:

0xffffffffUL >> (32 - param)

que es lo mismo que la respuesta de Julien arriba. Un problema con su respuesta es que si param es igual a 0 , produciendo la expresión 0xffffffffUL >> 32 , el resultado de la expresión sería 0xffffffffUL , en lugar del 0 esperado. (Por eso __ONE_COUNT__ mi parámetro como __ONE_COUNT__ para enfatizar su intención)

Para resolver este problema, simplemente podríamos agregar un caso especial para __ONE_COUNT igual a 0 usando if-else o ?: , Como esto:

#define BIT_MASK(__TYPE__, __ONE_COUNT__) / (((__ONE_COUNT__) != 0) / ? (((__TYPE__) -1) >> ((sizeof(__TYPE__) * CHAR_BIT) - (__ONE_COUNT__))) : 0)

Pero el código sin sucursales es mejor, ¿no? Vayamos a la siguiente parte:

((unsigned int) (-((__ONE_COUNT__) != 0)))

Comencemos desde la expresión más interna hasta la más externa. ((__ONE_COUNT__) != 0) produce 0 cuando el parámetro es 0 , o 1 contrario. (-((__ONE_COUNT__) != 0)) produce 0 cuando el parámetro es 0 , o -1 contrario. Para ((unsigned int) (-((__ONE_COUNT__) != 0))) , el truco de ((unsigned int) (-((__ONE_COUNT__) != 0))) tipos ((unsigned int) -1) ya se explicó anteriormente. ¿Te das cuenta el truco ahora? La expresion:

((__TYPE__) (-((__ONE_COUNT__) != 0)))

es igual a "all-0s" si __ONE_COUNT__ es cero, y "all-1s" en caso contrario. Actúa como una máscara de bits para el valor que calculamos en el primer paso. Entonces, si __ONE_COUNT__ no es cero, la máscara no tiene efecto y es la misma que la respuesta de Julien. Si __ONE_COUNT__ es 0 , __ONE_COUNT__ todos los bits de la respuesta de Julien, produciendo un cero constante. Para visualizar, mira esto:

__ONE_COUNT__ : 0 Other ------------- -------------- (__ONE_COUNT__) 0 = 0x000...0 (itself) ((__ONE_COUNT__) != 0) 0 = 0x000...0 1 = 0x000...1 ((__TYPE__) (-((__ONE_COUNT__) != 0))) 0 = 0x000...0 -1 = 0xFFF...F


¿Qué tal esto (en Java):

int mask = -1; mask = mask << param; mask = ~mask;

De esta manera, puede evitar las tablas de búsqueda y la codificación de la longitud de un entero.

Explicación: Un entero con signo con un valor de -1 se representa en binario como todos. Mueve a la izquierda el número dado de veces para agregar tantos 0 al lado derecho. Esto dará lugar a una especie de "máscara inversa". Luego niega el resultado cambiado para crear tu máscara.

Esto podría reducirse a:

int mask = ~(-1<<param);

Un ejemplo:

int param = 5; int mask = -1; // 11111111 (shortened for example) mask = mask << param; // 11100000 mask = ~mask; // 00011111


Alternativamente, puede usar un cambio a la derecha para evitar el problema mencionado en la solución (1 << param) - 1 .

unsigned long const mask = 0xffffffffUL >> (32 - param);

asumiendo que param <= 32 , por supuesto.


Para aquellos interesados, esta es la alternativa de la tabla de consulta que se comenta en los comentarios a la otra respuesta, la diferencia es que funciona correctamente para un parámetro de 32. Es bastante fácil extenderse a la versión unsigned long long 64 bits, si lo necesita. , y no debería ser significativamente diferente en velocidad (si se llama en un bucle interno estrecho, la tabla estática permanecerá en al menos el caché L2, y si no se llama en un bucle interno estrecho, la diferencia de rendimiento no será importante ).

unsigned long mask2(unsigned param) { static const unsigned long masks[] = { 0x00000000UL, 0x00000001UL, 0x00000003UL, 0x00000007UL, 0x0000000fUL, 0x0000001fUL, 0x0000003fUL, 0x0000007fUL, 0x000000ffUL, 0x000001ffUL, 0x000003ffUL, 0x000007ffUL, 0x00000fffUL, 0x00001fffUL, 0x00003fffUL, 0x00007fffUL, 0x0000ffffUL, 0x0001ffffUL, 0x0003ffffUL, 0x0007ffffUL, 0x000fffffUL, 0x001fffffUL, 0x003fffffUL, 0x007fffffUL, 0x00ffffffUL, 0x01ffffffUL, 0x03ffffffUL, 0x07ffffffUL, 0x0fffffffUL, 0x1fffffffUL, 0x3fffffffUL, 0x7fffffffUL, 0xffffffffUL }; if (param < (sizeof masks / sizeof masks[0])) return masks[param]; else return 0xffffffffUL; /* Or whatever else you want to do in this error case */ }

Vale la pena señalar que si necesita la instrucción if() (porque le preocupa que alguien pueda llamarla con param > 32 ), esto no le hace ganar nada sobre la alternativa de la otra respuesta:

unsigned long mask(unsigned param) { if (param < 32) return (1UL << param) - 1; else return -1; }

La única diferencia es que la última versión tiene un caso especial param >= 32 , mientras que la primera solo tiene un caso especial param > 32 .


Si está preocupado por el desbordamiento en un lenguaje similar a C con (1 << param) - 1 (cuando param es 32 o 64 en el tamaño de tamaño máximo, la máscara se convierte en 0 debido a que BitShift supera los límites del tipo), una solución Acabo de pensar en:

const uint32_t mask = ( 1ul << ( maxBits - 1ul ) ) | ( ( 1ul << ( maxBits - 1ul ) ) - 1ul );

U otro ejemplo

const uint64_t mask = ( 1ull << ( maxBits - 1ull ) ) | ( ( 1ull << ( maxBits - 1ull ) ) - 1ull );

Aquí hay una versión con plantilla, tenga en cuenta que debe usar esto con un tipo R sin firma:

#include <limits.h> /* CHAR_BIT */ // bits cannot be 0 template <typename R> static constexpr R bitmask1( const R bits ) { const R one = 1; assert( bits >= one ); assert( bits <= sizeof( R ) * CHAR_BIT ); const R bitShift = one << ( bits - one ); return bitShift | ( bitShift - one ); }

Digamos que max bits es 8 con un byte, con la primera función de desbordamiento tendríamos 1 << 8 == 256 , que cuando se convierte en byte se convierte en 0. Con mi función tenemos 1 << 7 == 128 , que El byte puede contener, por lo que se convierte en 1<<7 | 1<<7 - 1 1<<7 | 1<<7 - 1 .

No he compilado la función, por lo que puede contener errores tipográficos.

Y por diversión aquí está la carne de Julien Royer :

// bits can be 0 template <typename R> static constexpr R bitmask2( const R bits ) { const R zero = 0; const R mask = ~zero; const R maxBits = sizeof( R ) * CHAR_BIT; assert( bits <= maxBits ); return mask >> ( maxBits - bits ); }


Una cosa a tener en cuenta acerca de las máscaras de bits es que siempre son una menos de una potencia de dos.

La expresión "1 << n" es la manera fácil de obtener el n-ésimo poder de dos.

No desea que Zero proporcione una máscara de bits de "00000001", desea que proporcione cero. Así que necesitas restar uno.

mask = (1 << param) - 1;

Editar:

Si quieres un caso especial para param> 32:

int sizeInBits = sizeof(mask) * BITS_PER_BYTE; // BITS_PER_BYTE = 8; mask = (param >= sizeInBits ? -1 : (1 << param) - 1);

Este método debería funcionar para enteros de 16, 32 o 64 bits, pero es posible que tenga que escribir explícitamente el ''1''.