Отримати конкретний біт з байта


97

У мене є байт, зокрема один байт із байтового масиву, який надійшов через UDP, надісланий з іншого пристрою. Цей байт зберігає стан увімкнення / вимкнення 8 реле у пристрої.

Як отримати значення конкретного біта у згаданому байті? В ідеалі метод розширення виглядав би найелегантніше, і повернення буля було б для мене найбільш розумним.

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

Відповіді:


178

Легко. Використовуйте порозрядне І для порівняння вашого числа зі значенням 2 ^ bitNumber, яке можна дешево обчислити за допомогою бітового зсуву.

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

РЕДАГУВАТИ: Щоб додати трохи детальніше, оскільки існує багато подібних відповідей без пояснення:

Побітове І порівнює кожне число, побітове, використовуючи приєднання І, щоб отримати число, яке є комбінацією бітів, де були встановлені як перший біт, так і другий біт у цьому місці. Ось логічна матриця І логіки в "клюванні", яка показує роботу побітового І:

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

У вашому випадку ми порівнюємо передане вами число з числом, для якого встановлений лише біт, який ви хочете шукати. Скажімо, ви шукаєте четвертий біт:

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

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

Зміщення бітів, щоб отримати число, з яким ми хочемо порівняти, саме так і звучить: візьміть число, представлене як набір бітів, і змістіть ці біти вліво або вправо на певну кількість місць. Оскільки це двійкові числа, і тому кожен біт є на одну більшу потужність двох, ніж той, що знаходиться праворуч, зміщення бітів вліво еквівалентно подвоєнню числа один раз для кожного місця, яке переміщується, еквівалентно множенню числа на 2 ^ х. У вашому прикладі, шукаючи четвертий біт, ми виконуємо:

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

Тепер ви знаєте, як це робиться, що відбувається на низькому рівні і чому це працює.


8
Через відсутність фігурних дужок (пріоритет оператора) цей код не компілюється, він повинен бути var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk

54

Хоча добре читати та розуміти відповідь Джоша, ви, мабуть, будете щасливішими, використовуючи для цього клас, який надає Microsoft: System.Collections.BitArray Він доступний у всіх версіях .NET Framework.


1
Це казково, але я вважаю, що рішення Джоша набагато швидше та ефективніше.
Самуель Аллан,

1
@ user2332868: компілятор JIT спеціально розпізнає виклики певних функцій бібліотеки та генерує ефективний код, але я не уявляю, чи ці функції набувають любові.
Ben Voigt,

1
добре, щоб бути впевненим, що ви можете зробити те саме, що Джош, але в чистому вбудованому складі. Але, на жаль, я програмую лише в збірці NASM і не знаю про збірку C # компілюється в :(.
Семюел Аллан,

1
ІМХО це вбиває продуктивність для такого простого завдання. Використання побітової альтернативи оператора також дає вам інструмент, який ви можете використовувати майже будь-якою мовою =)
Gaspa79,

38

Це

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

я повинен це зробити, я думаю.


9

ще один спосіб зробити це :)

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

1
Це не спрацює: байт b = 1; return ((b >> 1) & 1)! = 0 (це дорівнює 0)
Рафаель Дієго Ніколетті

1
Хм-м-м ... я вас не розумію. Якщо байт b = 1, біт у позиції 0 дорівнює 1, заданий (b >> 0) & 1, і біт у будь-якій позиції, більшій або рівній 1, дорівнює 0, заданий як (b >> n) & 1 де n> = 1 також
PierrOz

Я не вірю, що його взагалі це робить @DavideAndrea, коментар, опублікований Рафаелем, передбачає, що самий правильний біт - це біт 1, але код PierrOz призначений для того, коли самий правильний біт - це біт 0. Якщо b було 2, тоді ((2 >> 1)&1)є 1і ((2 >> 0)&1)це 0тому, що 2 є00000010
Камерон Аавік


6

Використання класу BitArray та створення методу розширення, як пропонує OP:

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

9
Ні, будь ласка, не створюйте і не викидайте BitArray для кожного бітового тесту.
Ben Voigt

@BenVoigt, це метод розширення байта на запит OP. Де ви рекомендуєте зберігати екземпляр BitArray?
Jay Walker

2
Ви відхиляєте запит і говорите, не називайте його як метод у байті, а викликайте його на BitArray. Можливо, байтова змінна може повністю зникнути.
Бен Войгт,

Якщо ви хочете отримати n-й біт байта, ви, швидше за все, шукаєте продуктивність, і це вбиває його. В іншому випадку ви завжди можете зробити інші непотрібні речі, такі як Convert.ToString (num, 2) [bitNumber], і отримати його теж.
Gaspa79


3

Метод полягає у використанні іншого байта разом з побітовим І для маскування цільового біта.

Я використовував домовленість зі своїх класів тут, де "0" є найбільш значущим бітом, а "7" - найменшим.

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

Спробуйте код нижче. Різниця з іншими повідомленнями полягає в тому, що ви можете встановити / отримати кілька бітів за допомогою маски ( field). Маска для 4-го біта може бути 1 << 3, або 0x10, наприклад.

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

** Приклад **

        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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.