[c++] Comment définir, effacer et basculer un seul bit?



Answers

Utilisation de la bibliothèque C ++ standard: std::bitset<N> .

Ou la version Boost : boost::dynamic_bitset .

Il n'y a pas besoin de rouler les vôtres:

#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 autorise un jeu de bits de taille d'exécution par rapport à un jeu de bits de taille de compilation standard.

Question

Comment définir, effacer et basculer un peu en C / C ++?




De snip-c.zip de snip-c.zip:

/*
**  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 que vous semblez avoir des problèmes dans tous ceux-ci est "(1L << (posn))". Tout cela est de créer un masque avec un seul bit sur et qui fonctionnera avec n'importe quel type entier. L'argument "posn" spécifie la position où vous voulez le bit. Si posn == 0, alors cette expression évaluera à:

    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 l'opérateur tilde (~).

Une fois le masque créé, il est appliqué à l'argument comme vous le suggérez, en utilisant les opérateurs bit à bit et (&), (|) et xor (^). Comme le masque est de type long, les macros fonctionneront aussi bien sur char, short, int ou long.

L'essentiel est que c'est 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 l'une de ces macros avec des valeurs de masque explicites chaque fois que vous en avez besoin, mais pourquoi le faire? Rappelez-vous que la substitution de macro se produit dans le préprocesseur et que le code généré reflétera le fait que le compilateur considère les valeurs comme constantes. Il est donc tout aussi efficace d'utiliser les macros généralisées que de réinventer la roue. faire de la manipulation de bits.

Pas convaincu? Voici un peu de code de test - j'ai utilisé Watcom C avec une optimisation complète et sans utiliser _cdecl pour que le désassemblage qui en résulte 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] ------------------------------------------- ----------------------




Si vous faites beaucoup de twittling, vous pouvez utiliser des masques qui rendront le tout plus rapide. Les fonctions suivantes sont très rapides et restent flexibles (elles permettent de faire défiler les bits dans des cartes 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, 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 que vous passez. Notez que pour les petits processeurs endiens que les octets, mots, dwords, qwords, etc., se mappent correctement en mémoire (raison principale pour laquelle les petits processeurs endiens sont 'meilleurs' que les processeurs big-endian, ah, je ressens une guerre de flamme sur...).




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)))



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);
}



Visual C 2010, and perhaps many other compilers, have direct support for bit operations built in. Surprisingly, this works, even the sizeof() operator works properly.

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);
}

So, to your question, IsGph[i] =1, or IsGph[i] =0 make setting and clearing bools easy.

To find unprintable characters...

//  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;
    }
}

Note there is nothing "special" about this code. It treats a bit like an integer - which technically, it is. A 1 bit integer that can hold 2 values, and 2 values only.

I once used this approach to find duplicate loan records, where loan_number was the ISAM key, using the 6-digit loan number as an index into the bit array. Savagely fast, and after 8 months, proved that the mainframe system we were getting the data from was in fact malfunctioning. The simplicity of bit arrays makes confidence in their correctness very high - vs a searching approach for example.




J'utilise des macros définies dans un fichier d'en-tête pour gérer les bits 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)))

/* 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))



Ce programme est de changer tout bit de données de 0 à 1 ou 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);
        }
    }
}



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, au cours d'un débogage basé sur l'oscilloscope, j'ai été étonné de constater que ces méthodes ont un surcoût considérable dans les cycles CPU par rapport à écrire une valeur directement sur les registres PORTnSET / PORTnCLEAR du micro qui fait une réelle différence. les broches de basculement de fréquence ISR.

Pour ceux qui ne sont pas familiers: Dans mon exemple, le micro possède un registre général d'état des broches PORTn qui reflète les broches de sortie, ainsi faire PORTn | = BIT_TO_SET donne une lecture-modification-écriture à ce registre. Cependant, les registres PORTnSET / PORTnCLEAR prennent un '1' pour signifier "veuillez faire ce bit 1" (SET) ou "veuillez faire ce bit zéro" (CLEAR) et un '0' pour signifier "laisser la broche tout seul". 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.




Use one of the operators as defined here .

To set a bit, used int x = x | 0x?; where ? is the bit position in binary form.




Utilisez les opérateurs au niveau du bit: & |

Pour définir le dernier bit en 000b :

foo = foo | 001b

Pour vérifier le dernier bit dans foo :

if ( foo & 001b ) ....

Pour effacer le dernier bit de foo :

foo = foo & 110b

J'ai utilisé XXXb pour plus de clarté. Vous travaillerez probablement avec la représentation HEX, en fonction de la structure de données dans laquelle vous empaquetez des bits.




Essayez une de ces fonctions dans le langage C pour changer n bits:

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;
}



How do you set, clear, and toggle a single bit?

To address a common coding pitfall when attempting to form the mask:
1 is not always wide enough

What problems happen when number is a wider type than 1 ?
x may be too great for the shift 1 << x leading to undefined behavior (UB). Even if x is not too great, ~ may not flip enough most-significant-bits.

// 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

To insure 1 is wide enough:

Code could use 1ull or pedantically (uintmax_t)1 and let the compiler optimize.

number |= (1ull << x);
number |= ((uintmax_t)1 << x);

Or cast - which makes for coding/review/maintenance issues keeping the cast correct and up-to-date.

number |= (type_of_number)1 << x;

Or gently promote the 1 by forcing a math operation that is as least as wide as the type of number .

number |= (number*0 + 1) << x;

As with most bit manipulations, best to work with unsigned types rather than signed ones




Related