Holen Sie sich ein bestimmtes Bit aus Byte


97

Ich habe ein Byte, speziell ein Byte aus einem Byte-Array, das über UDP eingegangen ist und von einem anderen Gerät gesendet wurde. Dieses Byte speichert den Ein / Aus-Zustand von 8 Relais im Gerät.

Wie erhalte ich den Wert eines bestimmten Bits in diesem Byte? Im Idealfall würde eine Erweiterungsmethode am elegantesten aussehen, und die Rückgabe eines Bools wäre für mich am sinnvollsten.

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

Antworten:


178

Einfach. Verwenden Sie ein bitweises UND, um Ihre Zahl mit dem Wert 2 ^ bitNumber zu vergleichen, der durch Bitverschiebung kostengünstig berechnet werden kann.

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

EDIT: Um ein wenig mehr Details hinzuzufügen, da es viele ähnliche Antworten ohne Erklärung gibt:

Ein bitweises UND vergleicht jede Zahl bitweise mit einem UND-Join, um eine Zahl zu erzeugen, die die Kombination von Bits darstellt, bei denen sowohl das erste Bit als auch das zweite Bit an dieser Stelle gesetzt wurden. Hier ist die Logikmatrix der UND-Logik in einem "Halbbyte", die die Funktionsweise eines bitweisen UND zeigt:

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

In Ihrem Fall vergleichen wir die übergebene Nummer mit einer Nummer, die nur das Bit enthält, nach dem Sie suchen möchten. Angenommen, Sie suchen nach dem vierten Bit:

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

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

Bitverschiebung, um die Zahl zu erzeugen, mit der wir vergleichen möchten, ist genau das, wonach es sich anhört: Nehmen Sie die Zahl, die als Satz von Bits dargestellt wird, und verschieben Sie diese Bits um eine bestimmte Anzahl von Stellen nach links oder rechts. Da es sich um Binärzahlen handelt und jedes Bit eine größere Zweierpotenz als das rechts davon ist, entspricht eine Bitverschiebung nach links einer einmaligen Verdoppelung der Zahl für jede verschobene Stelle, was einer Multiplikation der Zahl mit entspricht 2 ^ x. In Ihrem Beispiel suchen wir nach dem vierten Bit:

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

Jetzt wissen Sie, wie es gemacht wird, was auf niedriger Ebene vor sich geht und warum es funktioniert.


8
Aufgrund fehlender Klammern (Operator-Priorität) wird dieser Code nicht kompiliert var bit = (b & (1 << bitNumber-1)) != 0.
Bitbonk

54

Es ist zwar gut, Joshs Antwort zu lesen und zu verstehen, aber Sie werden wahrscheinlich glücklicher sein, wenn Sie die von Microsoft für diesen Zweck bereitgestellte Klasse verwenden: System.Collections.BitArray Sie ist in allen Versionen von .NET Framework verfügbar.


1
Das ist fabelhaft, aber ich glaube, Joshs Lösung ist viel schneller und effizienter.
Samuel Allan

1
@ user2332868: Der JIT-Compiler erkennt speziell Aufrufe bestimmter Bibliotheksfunktionen und generiert effizienten Code, aber ich habe keine Ahnung, ob diese bestimmten Funktionen die Liebe bekommen.
Ben Voigt

1
Natürlich können Sie das Gleiche wie Josh tun, jedoch in reiner Inline-Montage. Aber leider programmiere ich nur in der NASM-Assembly und weiß nichts über die Assembly, in die C # kompiliert wird :(.
Samuel Allan

1
Meiner Meinung nach beeinträchtigt dies die Leistung für eine so einfache Aufgabe. Die Verwendung der bitweisen Operator-Alternative gibt Ihnen auch ein Werkzeug, das Sie in fast jeder Sprache verwenden können =)
Gaspa79

38

Dies

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

sollte es tun, denke ich.


9

eine andere Art es zu tun :)

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

1
Dies wird nicht funktionieren: Byte b = 1; return ((b >> 1) & 1)! = 0 (es ist gleich 0)
Rafael Diego Nicoletti

1
Hmmm ... ich verstehe dich nicht. Wenn Byte b = 1 ist, ist das Bit an Position 0 1, gegeben durch (b >> 0) & 1, und das Bit an jeder Position größer oder gleich 1 ist 0, gegeben durch (b >> n) & 1, wobei n> = 1 ist auch
PierrOz

Ich glaube nicht, dass er das überhaupt tut @DavideAndrea, der von Rafael gepostete Kommentar geht davon aus, dass das Bit ganz rechts Bit 1 ist, aber PierrOz 'Code ist für den Fall, dass das Bit ganz rechts Bit 0 ist. Wenn b 2 war, dann ((2 >> 1)&1)ist 1und ((2 >> 0)&1)ist, 0weil 2 ist00000010
Cameron Aavik


6

Die Verwendung der BitArray- Klasse und das Erstellen einer Erweiterungsmethode als OP schlägt vor:

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

9
Nein, bitte erstellen Sie kein BitArray für jeden Bittest und werfen Sie es weg.
Ben Voigt

@ BenVoigt, es ist eine Erweiterungsmethode für ein Byte pro OP-Anforderung. Wo empfehlen Sie das Speichern der BitArray-Instanz?
Jay Walker

2
Sie drücken sich gegen die Anforderung zurück und sagen, rufen Sie sie nicht wie eine Methode im Byte auf, sondern im BitArray. Möglicherweise kann die Byte-Variable vollständig verschwinden.
Ben Voigt

Wenn Sie das n-te Bit eines Bytes erhalten möchten, suchen Sie höchstwahrscheinlich nach Leistung, und dies bringt es zum Erliegen. Andernfalls könnten Sie immer andere unnötige Dinge wie Convert.ToString (num, 2) [bitNumber] tun und es auch bekommen.
Gaspa79


3

Die Methode besteht darin, ein anderes Byte zusammen mit einem bitweisen UND zu verwenden, um das Zielbit auszublenden.

Ich habe hier Konventionen aus meinen Klassen verwendet, wobei "0" das höchstwertige Bit und "7" das niedrigste ist.

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

Versuchen Sie den folgenden Code. Der Unterschied zu anderen Posts besteht darin, dass Sie mit einer Maske ( field) mehrere Bits setzen / abrufen können . Die Maske für das 4. Bit kann beispielsweise 1 << 3 oder 0x10 sein.

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

** Beispiel **

        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);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.