Bayttan belirli bir bit alın


98

Bir baytım var, özellikle başka bir aygıttan gönderilen UDP aracılığıyla gelen bir bayt dizisinden bir bayt. Bu bayt, cihazdaki 8 rölenin açık / kapalı durumunu depolar.

Belirtilen bayttaki belirli bir bitin değerini nasıl elde ederim? İdeal olarak, bir uzatma yöntemi en zarif görünecektir ve bir bool döndürmek bana en mantıklı olacaktır.

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

Yanıtlar:


182

Kolay. Sayınızı 2 ^ bitNumber değeriyle karşılaştırmak için bitsel AND kullanın; bu, bit kaydırma ile ucuz bir şekilde hesaplanabilir.

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

DÜZENLEME: Biraz daha fazla ayrıntı eklemek için, çünkü açıklaması olmayan birçok benzer yanıt var:

Bitsel AND, o yerdeki hem birinci bitin hem de ikinci bitin ayarlandığı bitlerin birleşimi olan bir sayı üretmek için bir VE birleşimini kullanarak her sayıyı bit bit karşılaştırır. İşte AND mantığının mantık matrisi, bitsel AND'nin işleyişini gösteren bir "yarım baytta":

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

Sizin durumunuzda, geçtiğiniz sayıyı sadece aramak istediğiniz biti içeren bir sayı ile karşılaştırıyoruz. Dördüncü kısmı aradığınızı varsayalım:

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

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

Karşılaştırmak istediğimiz sayıyı üretmek için bit kaydırma, kulağa tam olarak benzeyen şeydir: bir bit kümesi olarak gösterilen sayıyı alın ve bu bitleri belirli sayıda yer kadar sola veya sağa kaydırın. Bunlar ikili sayılar olduğundan ve bu nedenle her bit, sağındaki birinden ikinin bir büyük üssü olduğundan, sola bit kaydırma, kaydırılan her yer için sayının bir kez ikiye katlanmasına eşdeğerdir; 2 ^ x. Örneğinizde dördüncü biti ararken şunları gerçekleştiriyoruz:

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

Artık nasıl yapıldığını, düşük seviyede neler olduğunu ve neden işe yaradığını biliyorsunuz.


9
Eksik ayraçlar (operatör önceliği) nedeniyle bu kod derlenmez, olması gerekir var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Josh'un cevabını okumak ve anlamak iyi olsa da, Microsoft'un bu amaç için sağladığı sınıfı kullanarak muhtemelen daha mutlu olacaksınız: System.Collections.BitArray .NET Framework'ün tüm sürümlerinde mevcuttur.


1
Bu harika ama Josh'un çözümünün çok daha hızlı ve daha verimli olduğuna inanıyorum.
Samuel Allan

1
@ user2332868: JIT derleyicisi, belirli kütüphane işlevlerine yapılan çağrıları özel olarak tanır ve verimli kod üretir, ancak bu belirli işlevlerin sevgiyi alıp almadığı hakkında hiçbir fikrim yok.
Ben Voigt

1
Josh ile aynı şeyi yapabileceğinizden emin olun, ancak saf satır içi montajda. Ama NASM'ın içinde ne yazık ki ben yalnızca program montaj ve içine :( montaj C hakkında # derler bilmiyorum.
Samuel Allan

1
IMHO, bu kadar basit bir görev için performansı düşürür. Bitsel operatör alternatifini kullanmak size hemen hemen her dilde kullanabileceğiniz bir araç sağlar =)
Gaspa79

38

Bu

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

bence yapmalı.


10

bunu yapmanın başka bir yolu :)

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

1
Bu işe yaramaz: bayt b = 1; return ((b >> 1) & 1)! = 0 (0'a eşittir)
Rafael Diego Nicoletti

1
Hmmm ... Seni anlamıyorum. Bayt b = 1 ise, 0 konumundaki bit 1'dir, (b >> 0) & 1 ile verilir ve 1'den büyük veya eşit herhangi bir konumdaki bit 0, n> = 1 olduğunda (b >> n) & 1 ile verilir ayrıca
PierrOz

Bunu yaptığına inanmıyorum @DavideAndrea, Rafael tarafından gönderilen yorum en sağdaki bitin bit 1 olduğunu varsayıyor, ancak PierrOz'un kodu en sağdaki bit 0 olduğu zaman içindir. B 2 ise, o ((2 >> 1)&1)zaman 1ve ((2 >> 0)&1)olduğu 02 çünkü00000010
Cameron Aavik


7

BitArray sınıfını kullanmak ve OP'nin önerdiği gibi bir genişletme yöntemi yapmak:

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

11
Hayır, lütfen her bit testi için bir BitArray oluşturmayın ve atmayın.
Ben Voigt

@BenVoigt, OP isteği başına bayt üzerinde bir genişletme yöntemidir. BitArray örneğini nerede depolamanızı önerirsiniz?
Jay Walker

2
İsteği geri itiyorsunuz ve diyorsunuz ki, onu bayttaki bir yöntem gibi çağırmayın, BitArray'de çağırın. Belki bayt değişkeni tamamen ortadan kalkabilir.
Ben Voigt

Bir baytın n'inci bitini almak istiyorsanız, büyük olasılıkla performans arıyorsunuz ve bu onu öldürür. Aksi takdirde, Convert.ToString (num, 2) [bitNumber] gibi diğer gereksiz şeyleri her zaman yapabilir ve onu da alabilirsiniz.
Gaspa79


4

Yöntem, hedef biti maskelemek için bitsel AND ile birlikte başka bir bayt kullanmaktır.

Buradaki sınıflarımda "0" ın en önemli bit ve "7" nin en az olduğu kuralı kullandım.

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

4

Aşağıdaki kodu deneyin. Diğer gönderilerden farkı, maske ( field) kullanarak birden çok bit ayarlayabilmeniz / alabilmenizdir . 4. bit için maske, örneğin 1 << 3 veya 0x10 olabilir.

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

** Misal **

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

3
[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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.