Получить определенный бит из байта


97

У меня есть байт, в частности один байт из массива байтов, который поступил через UDP, отправленный с другого устройства. Этот байт хранит состояние включения / выключения 8 реле в устройстве.

Как мне получить значение определенного бита в указанном байте? В идеале метод расширения выглядел бы наиболее элегантно, и для меня было бы наиболее разумно возвращать bool.

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

Ответы:


179

Легко. Используйте побитовое И, чтобы сравнить ваше число со значением 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-компилятор специально распознает вызовы определенных библиотечных функций и генерирует эффективный код, но я понятия не имею, понравятся ли эти конкретные функции.
— Бен Фойгт

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
Это не сработает: byte 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, комментарий Написал Rafael предполагает самый правый бит бит 1, но код PierrOz является для того, когда самый правый бит бит 0. Если б было 2, то ((2 >> 1)&1)есть 1и ((2 >> 0)&1)is 0потому что 2 is00000010
— Кэмерон Аавик


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?
— Джей Уокер

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.