Comment régler, effacer et basculer un peu?
Comment régler, effacer et basculer un peu?
Réponses:
Utilisez l'opérateur OR au niveau du bit ( |
) pour définir un bit.
number |= 1UL << n;
Cela mettra le n
e bit de number
. n
devrait être zéro, si vous voulez régler le 1
bit st et ainsi de suite jusqu'à n-1
, si vous voulez régler le n
bit bit.
Utilisez 1ULL
si number
est plus large que unsigned long
; la promotion de 1UL << n
ne se produit qu'après avoir évalué 1UL << n
où le comportement indéfini se déplace de plus de la largeur de a long
. La même chose s'applique à tous les autres exemples.
Utilisez l'opérateur AND au niveau du bit ( &
) pour effacer un peu.
number &= ~(1UL << n);
Cela effacera le n
e bit de number
. Vous devez inverser la chaîne de bits avec l'opérateur NOT au niveau du bit ( ~
), puis AND.
L'opérateur XOR ( ^
) peut être utilisé pour basculer un peu.
number ^= 1UL << n;
Cela fera basculer le n
e bit de number
.
Vous ne l'avez pas demandé, mais je pourrais aussi bien l'ajouter.
Pour vérifier un peu, déplacez le nombre n vers la droite, puis au niveau du bit ET:
bit = (number >> n) & 1U;
Cela mettra la valeur du n
e bit de number
dans la variable bit
.
La définition du n
th bit sur 1
ou 0
peut être obtenue avec les éléments suivants sur une implémentation C ++ complémentaire à 2:
number ^= (-x ^ number) & (1UL << n);
Le bit n
sera défini si x
est 1
, et effacé si x
est 0
. Si x
a une autre valeur, vous obtenez des ordures. x = !!x
la booléenne à 0 ou 1.
Pour rendre cela indépendant du comportement de négation du complément à 2 (où -1
tous les bits sont définis, contrairement à une implémentation C ++ de complément ou de signe / magnitude), utilisez la négation non signée.
number ^= (-(unsigned long)x ^ number) & (1UL << n);
ou
unsigned long newbit = !!x; // Also booleanize to force 0 or 1
number ^= (-newbit ^ number) & (1UL << n);
C'est généralement une bonne idée d'utiliser des types non signés pour la manipulation de bits portables.
ou
number = (number & ~(1UL << n)) | (x << n);
(number & ~(1UL << n))
effacera le n
bit e et (x << n)
mettra le n
bit bit sur x
.
C'est aussi généralement une bonne idée de ne pas copier / coller de code en général et tant de gens utilisent des macros de préprocesseur (comme la réponse du wiki communautaire plus bas ) ou une sorte d'encapsulation.
bit = (number >> x) & 1
1
est un int
littéral, qui est signé. Donc, toutes les opérations ici fonctionnent sur des numéros signés, ce qui n'est pas bien défini par les normes. Les normes ne garantissent pas le complément à deux ou le décalage arithmétique, il est donc préférable de l'utiliser 1U
.
number = number & ~(1 << n) | (x << n);
changer le nième bit en x.
Utilisation de la bibliothèque standard C ++: std::bitset<N>
.
Ou la Boost version: boost::dynamic_bitset
.
Il n'est pas nécessaire de rouler le vôtre:
#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 version Boost permet un ensemble de bits de taille d'exécution par rapport à un ensemble de bits de taille de compilation standard de bibliothèque .
L'autre option consiste à utiliser des champs de bits:
struct bits {
unsigned int a:1;
unsigned int b:1;
unsigned int c:1;
};
struct bits mybits;
définit un champ de 3 bits (en fait, il s'agit de trois champs de 1 bit). Les opérations sur les bits deviennent maintenant un peu (haha) plus simples:
Pour régler ou effacer un peu:
mybits.b = 1;
mybits.c = 0;
Pour basculer un peu:
mybits.a = !mybits.a;
mybits.b = ~mybits.b;
mybits.c ^= 1; /* all work */
Vérification un peu:
if (mybits.c) //if mybits.c is non zero the next line below will execute
Cela ne fonctionne qu'avec des champs de bits de taille fixe. Sinon, vous devez recourir aux techniques de twiddling décrites dans les articles précédents.
J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits définis et effacer:
/* 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))
doit être ((x) & (y)) == (y)
sinon il retourne un résultat incorrect sur le masque multibit (ex. 5
vs 3
) / * Bonjour à tous les fossoyeurs:) * /
1
devrait être (uintmax_t)1
ou similaire dans le cas où quelqu'un essaie d'utiliser ces macros sur un long
type plus grand
BITMASK_CHECK_ALL(x,y)
peut être implémenté comme!~((~(y))|(x))
!(~(x) & (y))
Il vaut parfois la peine d'utiliser un enum
pour nommer les bits:
enum ThingFlags = {
ThingMask = 0x0000,
ThingFlag0 = 1 << 0,
ThingFlag1 = 1 << 1,
ThingError = 1 << 8,
}
Ensuite, utilisez les noms plus tard. C'est à dire écrire
thingstate |= ThingFlag1;
thingstate &= ~ThingFlag0;
if (thing & ThingError) {...}
pour régler, effacer et tester. De cette façon, vous masquez les nombres magiques du reste de votre code.
À part cela, j'approuve la solution de Jeremy.
clearbits()
fonction au lieu de &= ~
. Pourquoi utilisez-vous une énumération pour cela? Je pensais que c'était pour créer un tas de variables uniques avec une valeur arbitraire cachée, mais vous attribuez une valeur définie à chacune. Alors, quel est l'avantage par rapport à leur simple définition de variables?
enum
s pour des ensembles de constantes liées remonte très loin dans la programmation c. Je soupçonne qu'avec les compilateurs modernes, le seul avantage par rapport à const short
quoi que ce soit, c'est qu'ils sont explicitement regroupés. Et lorsque vous les voulez pour autre chose que des masques de bit, vous obtenez la numérotation automatique. En c ++ bien sûr, ils forment également des types distincts, ce qui vous permet de vérifier les erreurs statiques en plus.
enum ThingFlags
valeur ThingError|ThingFlag1
, par exemple?
int
. Cela peut provoquer toutes sortes de bogues subtils en raison de la promotion implicite d'entiers ou d'opérations au niveau du bit sur les types signés. thingstate = ThingFlag1 >> 1
invoquera par exemple un comportement défini par l'implémentation. thingstate = (ThingFlag1 >> x) << y
peut invoquer un comportement non défini. Etc. Pour être sûr, toujours cast dans un type non signé.
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, analysons les choses ...
L'expression courante avec laquelle vous semblez avoir des problèmes avec tous ces éléments est "(1L << (posn))". Tout cela ne fait que créer un masque avec un seul bit et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie la position où vous voulez que le bit. Si posn == 0, alors cette expression sera évaluée à:
0000 0000 0000 0000 0000 0000 0000 0001 binary.
Si posn == 8, il évaluera:
0000 0000 0000 0000 0000 0001 0000 0000 binary.
En d'autres termes, il crée simplement un champ de 0 avec un 1 à la position spécifiée. La seule partie délicate est dans la macro BitClr () où nous devons définir un seul bit 0 dans un champ de 1. Ceci est accompli en utilisant le complément 1 de la même expression que celle indiquée par l'opérateur tilde (~).
Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs au niveau du bit et (&), ou (|) et xor (^). Puisque le masque est de type long, les macros fonctionneront tout aussi bien sur les caractères, les courts, les int ou les longs.
L'essentiel est qu'il s'agit d'une solution générale à toute une classe de problèmes. Il est bien sûr possible et même approprié de réécrire l'équivalent de n'importe laquelle de ces macros avec des valeurs de masque explicites à chaque fois que vous en avez besoin, mais pourquoi le faire? N'oubliez pas que la substitution de macro se produit dans le préprocesseur et que le code généré reflétera le fait que les valeurs sont considérées comme constantes par le compilateur - c'est-à-dire qu'il est tout aussi efficace d'utiliser les macros généralisées que de "réinventer la roue" à chaque fois que vous en avez besoin. faire de la manipulation de bits.
Pas convaincu? Voici un code de test - j'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl pour que le démontage résultant soit aussi propre que possible:
---- [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 (démonté)] -------------------------------------- ---------
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
c'est le cas long long
. 1L
doit être le type le plus large possible, donc (uintmax_t)1
. (Vous pourriez vous en tirer 1ull
)
Utilisez les opérateurs au niveau du bit: &
|
Pour définir le dernier bit dans 000b
:
foo = foo | 001b
Pour archiver le dernier bit foo
:
if ( foo & 001b ) ....
Pour effacer le dernier bit dans foo
:
foo = foo & 110b
J'ai utilisé XXXb
pour plus de clarté. Vous travaillerez probablement avec la représentation HEX, selon la structure de données dans laquelle vous empaquetez les bits.
foo = foo ^ MY_MASK
foo = foo & ~MY_MASK
Pour le débutant, je voudrais expliquer un peu plus avec un exemple:
Exemple:
value is 0x55;
bitnum : 3rd.
L' &
opérateur est utilisé vérifier le 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)
Basculer ou retourner:
0101 0101
^
0000 1000
___________
0101 1101 (Flip the third bit without affecting other bits)
|
opérateur: définir le bit
0101 0101
|
0000 1000
___________
0101 1101 (set the third bit without affecting other bits)
Voici ma macro arithmétique de bits préférée, qui fonctionne pour tout type de tableau d'entiers non signés allant unsigned char
jusqu'à size_t
(qui est le plus grand type qui devrait être efficace pour travailler):
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op ((size_t)1<<((size_t)(b)%(8*sizeof *(a)))))
Pour régler un peu:
BITOP(array, bit, |=);
Pour effacer un peu:
BITOP(array, bit, &=~);
Pour basculer un peu:
BITOP(array, bit, ^=);
Pour tester un peu:
if (BITOP(array, bit, &)) ...
etc.
BITOP(array, bit++, |=);
en boucle ne fera probablement pas ce que l'appelant veut.
BITCELL(a,b) |= BITMASK(a,b);
(les deux prennent a
comme argument pour déterminer la taille, mais ce dernier n'évaluerait jamais a
depuis il n'apparaît qu'en sizeof
).
(size_t)
distribution semble être là uniquement pour assurer des mathématiques non signées avec %
. Pourrait (unsigned)
là.
(size_t)(b)/(8*sizeof *(a))
pourrait inutilement rétrécir b
avant la division. Seul problème avec les tableaux de bits très volumineux. Encore une macro intéressante.
Comme cela est étiqueté «intégré», je suppose que vous utilisez un microcontrôleur. Toutes les suggestions ci-dessus sont valables et fonctionnent (lecture-modification-écriture, unions, structures, etc.).
Cependant, lors d'un débogage basé sur un oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à l'écriture d'une valeur directement dans les registres PORTnSET / PORTnCLEAR du micro, ce qui fait une réelle différence là où il y a des boucles serrées / hautes broches de basculement de l'ISR de fréquence.
Pour ceux qui ne sont pas familiers: Dans mon exemple, le micro a un registre d'état de broche général PORTn qui reflète les broches de sortie, ce qui fait que PORTn | = BIT_TO_SET entraîne une lecture-modification-écriture dans ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un «1» pour signifier «veuillez mettre ce bit à 1» (SET) ou «veuillez mettre ce bit à zéro» (CLEAR) et un «0» pour signifier «laisser la broche tranquille». ainsi, vous vous retrouvez avec deux adresses de port selon que vous définissez ou effacez le bit (pas toujours pratique) mais une réaction beaucoup plus rapide et un code assemblé plus petit.
volatile
et par conséquent le compilateur n'est pas en mesure d'effectuer des optimisations sur le code impliquant ces registres. Par conséquent, il est recommandé de démonter un tel code et de voir comment il s'est avéré au niveau de l'assembleur.
L'approche par champ de bits présente d'autres avantages dans l'arène intégrée. Vous pouvez définir une structure qui mappe directement sur les bits d'un registre matériel particulier.
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;
Vous devez être conscient de l'ordre de compression des bits - je pense que c'est MSB d'abord, mais cela peut dépendre de l'implémentation. Vérifiez également comment vos gestionnaires de compilateur gèrent les champs en franchissant les limites d'octets.
Vous pouvez ensuite lire, écrire, tester les valeurs individuelles comme précédemment.
#define bit_test(x, y) ( ( ((const char*)&(x))[(y)>>3] & 0x80 >> ((y)&0x07)) >> (7-((y)&0x07) ) )
Exemple d'utilisation:
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;
}
Remarques: Ceci est conçu pour être rapide (compte tenu de sa flexibilité) et non ramifié. Il en résulte un code machine SPARC efficace lors de la compilation de Sun Studio 8; Je l'ai également testé en utilisant MSVC ++ 2008 sur amd64. Il est possible de créer des macros similaires pour définir et effacer des bits. La principale différence de cette solution par rapport à beaucoup d'autres ici est qu'elle fonctionne pour n'importe quel emplacement dans à peu près n'importe quel type de variable.
Plus général, pour les bitmaps de taille arbitraire:
#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
est déjà défini par limits.h
, vous n'avez pas besoin de mettre le vôtre BITS
(et en fait vous aggravez votre code en le faisant)
Ce programme consiste à changer tout bit de données de 0 à 1 ou de 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);
}
}
}
Si vous faites beaucoup de twiddling, vous voudrez peut-être utiliser des masques qui rendront le tout plus rapide. Les fonctions suivantes sont très rapides et toujours flexibles (elles permettent le twiddling de bits dans des bit maps de n'importe quelle taille).
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;
}
Remarque, pour définir le bit 'n' dans un entier de 16 bits, vous procédez comme suit:
TSetBit( n, &my_int);
C'est à vous de vous assurer que le nombre de bits est dans la plage de la carte binaire que vous passez. Notez que pour les petits processeurs endian qui octets, mots, dwords, qwords, etc., se mappent correctement les uns aux autres en mémoire (raison principale pour laquelle les petits processeurs endian sont `` meilleurs '' que les processeurs big-endian, ah, je sens une guerre des flammes venir sur...).
Utilisez ceci:
int ToggleNthBit ( unsigned char n, int num )
{
if(num & (1 << n))
num &= ~(1 << n);
else
num |= (1 << n);
return num;
}
Élargir la bitset
réponse:
#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 vous souhaitez effectuer toutes ces opérations avec la programmation C dans le noyau Linux, je suggère d'utiliser les API standard du noyau Linux.
Voir 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
Remarque: Ici, toute l'opération se déroule en une seule étape. Ainsi, tous ces éléments sont garantis atomiques même sur les ordinateurs SMP et sont utiles pour maintenir la cohérence entre les processeurs.
Visual C 2010, et peut-être de nombreux autres compilateurs, prennent directement en charge les opérations booléennes intégrées. Un bit a deux valeurs possibles, tout comme un booléen, afin que nous puissions utiliser des booléens à la place - même s'ils occupent plus d'espace qu'un seul bit dans mémoire dans cette représentation. Cela fonctionne, même l' sizeof()
opérateur fonctionne correctement.
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);
}
Donc, à votre question, IsGph[i] =1
ou IsGph[i] =0
simplifiez le réglage et la suppression des bools.
Pour rechercher des caractères non imprimables:
// 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;
}
}
Notez qu'il n'y a rien de "spécial" dans ce code. Il traite un peu comme un entier - ce qui est techniquement le cas. Un entier de 1 bit pouvant contenir 2 valeurs et 2 valeurs uniquement.
J'ai déjà utilisé cette approche pour trouver des enregistrements de prêt en double, où loan_number était la clé ISAM, en utilisant le numéro de prêt à 6 chiffres comme index dans le tableau de bits. Sauvagement rapide et après 8 mois, a prouvé que le système mainframe dont nous obtenions les données fonctionnait en fait mal. La simplicité des tableaux de bits rend la confiance dans leur exactitude très élevée - par rapport à une approche de recherche par exemple.
bool
. Peut-être même 4 octets pour les configurations C89 qui utilisent int
pour implémenterbool
Voici quelques macros que j'utilise:
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 utilisée
int value, pos;
value - Data
pos - position du bit que nous souhaitons régler, effacer ou basculer.
Réglez un peu:
value = value | 1 << pos;
Clair un peu:
value = value & ~(1 << pos);
Basculez un peu:
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
peut être bool
.
Supposons d'abord quelques éléments
num = 55
Entier pour effectuer des opérations au niveau du bit (définir, obtenir, effacer, basculer).
n = 4
Position de bit basée sur 0 pour effectuer des opérations au niveau du bit.
nth
bit de décalage à droite num num
, n
fois. Effectuez ensuite ET au niveau du bit &
avec 1.bit = (num >> n) & 1;
Comment ça fonctionne?
0011 0111 (55 in decimal)
>> 4 (right shift 4 times)
-----------------
0000 0011
& 0000 0001 (1 in decimal)
-----------------
=> 0000 0001 (final result)
n
fois. Effectuez ensuite l'opération OR au niveau du bit |
avec num
.num |= (1 << n); // Equivalent to; num = (1 << n) | num;
Comment ça fonctionne?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
0001 0000
| 0011 0111 (55 in decimal)
-----------------
=> 0001 0000 (final result)
n
fois ie1 << n
.~ (1 << n)
.&
avec le résultat ci-dessus et num
. Les trois étapes ci-dessus peuvent être écrites ensemble num & (~ (1 << n))
;num &= (~(1 << n)); // Equivalent to; num = num & (~(1 << n));
Comment ça fonctionne?
0000 0001 (1 in decimal)
<< 4 (left shift 4 times)
-----------------
~ 0001 0000
-----------------
1110 1111
& 0011 0111 (55 in decimal)
-----------------
=> 0010 0111 (final result)
Pour basculer un peu, nous utilisons XOR au niveau du bit ^
opérateur . L'opérateur XOR au niveau du bit est évalué à 1 si le bit correspondant des deux opérandes est différent, sinon il est évalué à 0.
Cela signifie que pour basculer un peu, nous devons effectuer une opération XOR avec le bit que vous souhaitez basculer et 1.
num ^= (1 << n); // Equivalent to; num = num ^ (1 << n);
Comment ça fonctionne?
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)
Lecture recommandée - Exercices d'opérateur au niveau du bit
Comment définir, effacer et basculer un seul bit?
Pour résoudre un problème de codage courant lors de la tentative de formation du masque:
1
n'est pas toujours suffisamment large
Quels problèmes se produisent quand number
est un type plus large que 1
?
x
peut être trop important pour le changement 1 << x
conduisant à un comportement indéfini (UB). Même s'il x
n'est pas trop grand, il ~
peut ne pas retourner suffisamment de bits de poids fort.
// 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
Pour assurer que 1 est suffisamment large:
Le code pourrait utiliser 1ull
ou pédantiquement (uintmax_t)1
et laisser le compilateur optimiser.
number |= (1ull << x);
number |= ((uintmax_t)1 << x);
Ou cast - ce qui rend les problèmes de codage / révision / maintenance en gardant le cast correct et à jour.
number |= (type_of_number)1 << x;
Ou promouvez doucement le 1
en forçant une opération mathématique au moins aussi large que le type de number
.
number |= (number*0 + 1) << x;
Comme avec la plupart des manipulations de bits, à mieux travailler avec non signés types plutôt que signés les
number |= (type_of_number)1 << x;
ni number |= (number*0 + 1) << x;
approprié pour définir le bit de signe d'un type signé ... En fait, ni l'un ni l'autre number |= (1ull << x);
. Existe-t-il un moyen portable de le faire par position?
Une version basée sur un modèle C ++ 11 (placée dans un en-tête):
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;}
}
;
après vos définitions de fonction?)
(variable & bits == bits)
?
((variable & bits) == bits)
std::bitset
en c ++ 11
Ce programme est basé sur la solution ci-dessus de @ Jeremy. Si quelqu'un souhaite jouer rapidement.
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
Essayez l'une de ces fonctions en langage C pour changer 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
peut provoquer un comportement indéfini