Obtenez un bit spécifique de l'octet


97

J'ai un octet, en particulier un octet d'un tableau d'octets qui est entré via UDP envoyé depuis un autre appareil. Cet octet stocke l'état marche / arrêt de 8 relais dans l'appareil.

Comment obtenir la valeur d'un bit spécifique dans ledit octet? Idéalement, une méthode d'extension aurait l'air la plus élégante et renvoyer un booléen serait le plus logique pour moi.

public static bool GetBit(this byte b, int bitNumber)
{
    //black magic goes here
}

Réponses:


178

Facile. Utilisez un ET au niveau du bit pour comparer votre nombre avec la valeur 2 ^ bitNumber, qui peut être calculée à moindre coût par décalage de bits.

//your black magic
var bit = (b & (1 << bitNumber-1)) != 0;

EDIT: Pour ajouter un peu plus de détails car il y a beaucoup de réponses similaires sans explication:

Un ET au niveau du bit compare chaque nombre, bit par bit, à l'aide d'une jointure ET pour produire un nombre qui est la combinaison de bits où le premier bit et le deuxième bit à cet endroit ont été définis. Voici la matrice logique de la logique ET dans un "quartet" qui montre le fonctionnement d'un ET au niveau du bit:

  0101
& 0011
  ----
  0001 //Only the last bit is set, because only the last bit of both summands were set

Dans votre cas, nous comparons le nombre que vous avez passé avec un nombre dont seul le bit que vous souhaitez rechercher est défini. Disons que vous recherchez le quatrième bit:

  11010010
& 00001000
  --------
  00000000 //== 0, so the bit is not set

  11011010
& 00001000
  --------
  00001000 //!= 0, so the bit is set

Le décalage de bits, pour produire le nombre que nous voulons comparer, est exactement ce à quoi cela ressemble: prenez le nombre, représenté comme un ensemble de bits, et décalez ces bits vers la gauche ou la droite d'un certain nombre de places. Parce que ce sont des nombres binaires et que chaque bit est donc une puissance de deux supérieure à celle à sa droite, le décalage de bits vers la gauche équivaut à doubler le nombre une fois pour chaque lieu décalé, ce qui équivaut à multiplier le nombre par 2 ^ x. Dans votre exemple, à la recherche du quatrième bit, nous effectuons:

       1 (2^0) << (4-1) ==        8 (2^3)
00000001       << (4-1) == 00001000

Vous savez maintenant comment cela se fait, ce qui se passe au bas niveau et pourquoi cela fonctionne.


8
En raison des accolades manquantes (priorité des opérateurs), ce code ne se compile pas, il doit l'être var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Bien qu'il soit bon de lire et de comprendre la réponse de Josh, vous serez probablement plus heureux d'utiliser la classe fournie par Microsoft à cet effet: System.Collections.BitArray Il est disponible dans toutes les versions de .NET Framework.


1
C'est fabuleux mais je crois que la solution de Josh est beaucoup plus rapide et efficace.
Samuel Allan

1
@ user2332868: le compilateur JIT reconnaît spécialement les appels à certaines fonctions de la bibliothèque et génère du code efficace, mais je n'ai aucune idée si ces fonctions particulières obtiennent l'amour.
Ben Voigt

1
bien pour être sûr que vous pouvez faire la même chose que Josh mais en assemblage en ligne pur. Mais malheureusement, je programme uniquement en assemblage NASM et je ne sais pas à propos de l'assemblage compilé C # dans :(.
Samuel Allan

1
IMHO cela tue la performance pour une tâche aussi simple. L'utilisation de l'alternative d'opérateur au niveau du bit vous donne également un outil que vous pouvez utiliser dans presque toutes les langues =)
Gaspa79

38

Ce

public static bool GetBit(this byte b, int bitNumber) {
   return (b & (1 << bitNumber)) != 0;
}

devrait le faire, je pense.


9

une autre façon de faire :)

return ((b >> bitNumber) & 1) != 0;

1
Cela ne fonctionnera pas: octet b = 1; return ((b >> 1) & 1)! = 0 (c'est égal à 0)
Rafael Diego Nicoletti

1
Hmmm ... je ne vous comprends pas. Si l'octet b = 1, le bit à la position 0 est 1, donné par (b >> 0) & 1, et le bit à toute position supérieure ou égale à 1 est 0, donné par (b >> n) & 1 où n> = 1 aussi
PierrOz

Je ne crois pas qu'il le fasse du tout @DavideAndrea, le commentaire posté par Rafael suppose que le bit le plus à droite est le bit 1, mais le code de PierrOz est pour quand le bit le plus à droite est le bit 0. Si b était 2, alors ((2 >> 1)&1)est 1et ((2 >> 0)&1)est 0parce que 2 est00000010
Cameron Aavik


6

Utilisation de la classe BitArray et création d'une méthode d'extension comme OP le suggère:

public static bool GetBit(this byte b, int bitNumber)
{
    System.Collections.BitArray ba = new BitArray(new byte[]{b});
    return ba.Get(bitNumber);
}

9
Non, veuillez ne pas créer et jeter un BitArray pour chaque test de bit.
Ben Voigt

@BenVoigt, c'est une méthode d'extension sur un octet par requête OP. Où recommandez-vous de stocker l'instance BitArray?
Jay Walker

2
Vous repoussez la demande et dites, ne l'appelez pas comme une méthode sur l'octet, appelez-la sur le BitArray. Peut-être que la variable byte peut complètement disparaître.
Ben Voigt le

Si vous cherchez à obtenir le nième bit d'octet, vous recherchez probablement des performances, et cela les tue. Sinon, vous pourriez toujours faire d'autres choses inutiles comme Convert.ToString (num, 2) [bitNumber] et l'obtenir aussi.
Gaspa79


3

La méthode consiste à utiliser un autre octet avec un ET au niveau du bit pour masquer le bit cible.

J'ai utilisé la convention de mes classes ici où "0" est le bit le plus significatif et "7" est le moins.

public static class ByteExtensions
{
    // Assume 0 is the MSB andd 7 is the LSB.
    public static bool GetBit(this byte byt, int index)
    {
        if (index < 0 || index > 7)
            throw new ArgumentOutOfRangeException();

        int shift = 7 - index;

        // Get a single bit in the proper position.
        byte bitMask = (byte)(1 << shift);

        // Mask out the appropriate bit.
        byte masked = (byte)(byt & bitMask);

        // If masked != 0, then the masked out bit is 1.
        // Otherwise, masked will be 0.
        return masked != 0;
    }
}

3

Essayez le code ci-dessous. La différence avec d'autres articles est que vous pouvez définir / obtenir plusieurs bits en utilisant un masque ( field). Le masque pour le 4ème bit peut être 1 << 3 ou 0x10, par exemple.

    public int SetBits(this int target, int field, bool value)
    {
        if (value) //set value
        {
            return target | field;
        }
        else //clear value
        {
            return target & (~field);
        }
    }

    public bool GetBits(this int target, int field)
    {
        return (target & field) > 0;
    }

** Exemple **

        bool is_ok = 0x01AF.GetBits(0x10); //false
        int res = 0x01AF.SetBits(0x10, true);
        is_ok = res.GetBits(0x10);  // true

2
[Flags]
enum Relays : byte
{
    relay0 = 1 << 0,
    relay1 = 1 << 1,
    relay2 = 1 << 2,
    relay3 = 1 << 3,
    relay4 = 1 << 4,
    relay5 = 1 << 5,
    relay6 = 1 << 6,
    relay7 = 1 << 7
}

public static bool GetRelay(byte b, Relays relay)
{
    return (Relays)b.HasFlag(relay);
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.