从字节中获取特定位


97

我有一个字节,特别是字节数组中的一个字节,该字节数组是通过另一设备发送的UDP传入的。该字节存储设备中8个继电器的开/关状态。

如何获得所述字节中特定位的值?理想情况下,扩展方法看起来最优雅,而返回布尔值对我来说最有意义。

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

Answers:


178

简单。使用按位与将您的数字与2 ^ bitNumber的值进行比较,可以通过移位将其便宜地计算出来。

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

编辑:添加更多的细节,因为有很多类似的答案而没有解释:

逐位AND使用AND连接逐位比较每个数字,以产生一个数字,该数字是在该位置的第一位和第二位都被设置的位的组合。这是“半字节”中AND逻辑的逻辑矩阵,显示了按位AND的操作:

  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的幂,因此向左移位等于对每个移位的位置将数字加倍一次,即等于将该数字乘以2 ^ x。在您的示例中,寻找第四位,我们执行:

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

现在,您知道它是如何完成的,在低层发生了什么以及为什么起作用。


8
由于缺少花括号(运算符优先级),因此该代码无法编译,因此必须为var bit = (b & (1 << bitNumber-1)) != 0;
bitbonk 2012年

54

虽然阅读和理解Josh的回答很好,但是使用Microsoft为此目的提供的类可能会更高兴:System.Collections.BitArray 在.NET Framework的所有版本中都可用。


1
这太棒了,但我相信Josh的解决方案会更快,更高效。
塞缪尔·艾伦

1
@ user2332868:JIT编译器确实可以识别对某些库函数的调用并生成有效的代码,但是我不知道这些特定函数是否能引起人们的广泛关注。
Ben Voigt 2014年

1
可以肯定的是,您可以执行与Josh相同的操作,但是可以使用纯内联汇编。但可悲的是,我只在NASM程序组装和不知道的装配C#编译成:(。
塞缪尔·艾伦

1
恕我直言,这扼杀了如此简单任务的性能。使用按位运算符替代方法还可以为您提供一种几乎可以在任何语言中使用的工具=)
Gaspa79 2015年

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 2014年

我不相信他会那样做,@ DavideAndrea,Rafael发表的评论假定最右边的位是第1位,但是PierrOz的代码是针对最右边的位是第0位。如果b是2,则((2 >> 1)&1)1((2 >> 0)&1)0因为2是00000010
Cameron Aavik


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 2014年

@BenVoigt,它是每个OP请求在字节上的扩展方法。您建议在哪里存储BitArray实例?
杰伊·沃克

2
您回退该请求,然后说,不要像在字节上那样调用它,而要在BitArray上调用它。也许字节变量可以完全消失。
Ben Voigt 2014年

如果您要获取字节的第n位,则很可能是在寻找性能,这会杀死它。否则,您总是可以做其他不必要的事情,例如Convert.ToString(num,2)[bitNumber]并获取它。
加斯帕79年


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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.