屏蔽时,可以使用哪些逻辑运算来忽略不相关的位?

Which logical operations can I use to ignore irrelevant bits when masking?

提问人:George Kerwood 提问时间:12/9/2020 更新时间:11/3/2023 访问量:296

问:

上下文

假设我有一个由 8 个布尔变量组成的系统模型。它们共同组成一个字节,可以表示我的系统的 128 种状态排列。设这个字节为 ,其中每个位都是我的变量之一。stateByte

现在,假设我有一些可枚举的状态,例如:

public enum States
{
    READY     = 0b_0000_0001
    OPERATING = 0b_0100_0000
    FAULT     = 0b_1000_0000
}

如果每个都是离散的,我可以很容易地确定,但是我的问题是:StatesStates currentState = (States)stateByte

我的状态仅依赖于特定位的子集,而不是整个字节。具体来说,根据状态的不同,有些位是无关紧要的。为了使用伪表示法,我有以下场景,其中注释了一个不相关的位:x

public enum States
{
    READY     = 0b_0000_0001 // Exactly this permutation
    OPERATING = 0b_0100_0000 // Exactly this permutation
    FAULT     = 0b_1xxx_xxxx // Only bit 7 need be high to determine a fault
}

问题

如何使用逻辑按位运算符(屏蔽)来仅枚举相关位的状态?

更多背景

对于那些会质疑我为什么要这样做或为什么我不能简单地使用阈值的细节,请参阅下面我正在集成的硬件的完整状态表:

enter image description here

C# 操作 逻辑 位掩码 布尔运算

评论

1赞 Fildor 12/9/2020
逻辑 AND 运算符 &
1赞 Thomas Koelle 12/9/2020
问题被标记为 C#,所以这里是标准的 flags 属性 learn.microsoft.com/en-us/dotnet/api/...
0赞 George Kerwood 12/9/2020
@ThomasKoelle 乍一看,我认为这可能正是我所需要的。我以前从未遇到过他们,谢谢!如果您有时间提供完整的答案并举例说明,我将不胜感激。

答:

4赞 blenderfreaky 12/9/2020 #1

您可以使用二进制和运算符来屏蔽值,例如仅包含某些位:&

0b_1xxx_xxxx & 0b_1000_0000 = 0b_1000_0000
0b_1xxx_xxxx & (1 << 7)     = 0b_1000_0000
0b_1xxx_xxxx & States.Fault = 0b_1000_0000

如果你想经常访问某些位,你可以编写一个扩展方法,如下所示:

public static boolean GetBit(this byte bitmask, int index) =>
    ((bitmask >> index) & 1) != 0;

0b_1xxx_xxxx.GetBit(7) = true

如果要一次检查多个位,则可以使用与要检查的所有位匹配的模式,并将它们与包含所有“正确”位和其他任何地方的 0 的另一个模式进行比较:

   0b_x0xx_1000
 & 0b_0100_1111  // Only look at bits 0-3 and 6
== 0b_0000_1000  // Check that bit 6 is 0, 3 is 1 and 0-2 are 0
                 // Other bits are 0 due to the logical and

评论

0赞 George Kerwood 12/9/2020
您好,感谢您的输入!如果不是特别低 (0) 位,就这么简单。例如,位 7 必须为高位,位 6 必须为低位,其他都无关紧要。我敢肯定,我缺少的补语有一些技巧。我试图避免一点一点地检查......
0赞 blenderfreaky 12/9/2020
怎么办?0b_1xxx_xxxx & 0b_1100_0000 == 0b_1000_0000
1赞 George Kerwood 12/9/2020
我想你仍然错过了问题的症结,我的朋友。我包含的表格可能比我写的表格更好。查看故障状态,其中位 0-2 == 0、位 3 == 1、位 6 == 0 和所有其他位都无关紧要。无论不相关的位值如何,我们需要的操作都必须产生相同的值,并且不会单独执行。
0赞 blenderfreaky 12/9/2020
这似乎是可以解决的,就像我之前的评论一样:x & 0b_0100_1111 == 0b_0000_1000
1赞 George Kerwood 12/9/2020
右!!!好的,我现在已经赶上了。对不起,我误解了你最初的回答。根据你的方法,我会为每个状态设置一个相应的掩码,在&之前应用,以便“强行拉下”不相关的位。这应该有效!如果你能稍微澄清一下你的答案,我会把它标记为已回答。谢谢
4赞 Thomas Koelle 12/9/2020 #2

如果标志解决方案有效,那么它将像这样完成:

    [Flags]
    public enum States
    {
        READY = 0b_0000_0001,
        OPERATING = 0b_0100_0000,
        FAULT = 0b_1000_0000
    }
    static void Main(string[] args)
    {
        var s = (States)5;
        var check = s = States.FAULT | States.OPERATING;
    }
0赞 Steven Rands 11/3/2023 #3

受到这个问题的其他答案之一的启发,我创建了以下扩展方法:

public static class ByteExtensions
{
    public static bool MatchesBitPattern(this byte value, string pattern)
    {
        if (pattern == null || !Regex.IsMatch(pattern, "^[01X]{8}$"))
        {
            throw new ArgumentException("Value must be an 8-character string containing only the characters 0, 1 or X.", nameof(pattern));
        }

        byte mask = Convert.ToByte(pattern.Replace('0', '1').Replace('X', '0'), 2);
        byte result = Convert.ToByte(pattern.Replace('X', '0'), 2);
        return (value & mask) == result;
    }
}

你会使用这样的东西:

internal class Program
{
    static void Main()
    {
        string pattern = "0XX1XX00";
        byte b;
        b =            0b_11010100;
        Console.WriteLine(b.MatchesBitPattern(pattern));  // false
        b =            0b_01010100;
        Console.WriteLine(b.MatchesBitPattern(pattern));  // true
    }
}