按位运算符的真实世界用例

时间:2010-01-19 20:45:26

标签: language-agnostic bitwise-operators

以下按位运算符的一些实际用例是什么?

  • XOR
  • 不是

42 个答案:

答案 0 :(得分:198)

  • 位字段(标记)
    它们是表示状态由几个“是或否”属性定义的事物的最有效方式。 ACL就是一个很好的例子;如果您说4个离散权限(读取,写入,执行,更改策略),最好将其存储在1个字节而不是浪费4.这些可以映射到多种语言的枚举类型,以增加方便性。

  • 通过端口/插槽进行通信
    始终涉及校验和,奇偶校验,停止位,流控制算法等,这通常取决于各个字节的逻辑值而不是数值,因为介质一次只能传输一位。

  • 压缩,加密
    这两者都严重依赖于按位算法。查看deflate算法的示例 - 所有内容都是位,而不是字节。

  • 有限状态机
    我主要讲的是嵌入在某些硬件中的那种,尽管它们也可以在软件中找到。它们本质上是组合的 - 它们可能实际上被“编译”到一堆逻辑门,所以它们必须表示为ANDORNOT等。 / p>

  • <强>图形 这里几乎没有足够的空间进入图形编程中使用这些运算符的每个区域。 XOR(或^)在这里特别有趣,因为第二次应用相同的输入将撤消第一次。较旧的GUI过去依赖于此选择突出显示和其他叠加,以消除昂贵的重绘需求。它们在慢速图形协议(即远程桌面)中仍然有用。

这些只是我提出的前几个例子 - 这不是一个详尽的清单。

答案 1 :(得分:43)

奇怪吗?

(value & 0x1) > 0

可以被两个(偶数)整除吗?

(value & 0x1) == 0

答案 2 :(得分:24)

低级编程就是一个很好的例子。例如,您可能需要将特定位写入内存映射寄存器,以使某些硬件按您的要求执行:

volatile uint32_t *register = (volatile uint32_t *)0x87000000;
uint32_t          value;
uint32_t          set_bit   = 0x00010000;
uint32_t          clear_bit = 0x00001000;

value = *register;            // get current value from the register
value = value & ~clear_bit;   // clear a bit
value = value | set_bit;      // set a bit
*register = value;            // write it back to the register

此外,htonl()htons()是使用&|运算符实现的(在endianness(字节顺序)与网络不匹配的计算机上顺序):

#define htons(a) ((((a) & 0xff00) >> 8) | \
                  (((a) & 0x00ff) << 8))

#define htonl(a) ((((a) & 0xff000000) >> 24) | \
                  (((a) & 0x00ff0000) >>  8) | \
                  (((a) & 0x0000ff00) <<  8) | \
                  (((a) & 0x000000ff) << 24))

答案 3 :(得分:23)

这里有一些常见的习惯用法处理存储为单个位的标志。

enum CDRIndicators {
  Local = 1 << 0,
  External = 1 << 1,
  CallerIDMissing = 1 << 2,
  Chargeable = 1 << 3
};

unsigned int flags = 0;

设置Chargeable标志:

flags |= Chargeable;

清除CallerIDMissing标志:

flags &= ~CallerIDMissing;

测试是否设置了CallerIDMissing和Chargeable:

if((flags & (CallerIDMissing | Chargeable )) == (CallerIDMissing | Chargeable)) {

}

答案 4 :(得分:21)

我使用它们来获取打包颜色值的RGB(A)值。

答案 5 :(得分:20)

我在实现CMS的安全模型时使用了按位操作。如果用户位于适当的组中,则该页面可供用户访问。用户可以在多个组中,因此我们需要检查用户组和页面组之间是否存在交集。因此,我们为每个组分配了一个唯一的2次幂标识符,例如:

Group A = 1 --> 00000001
Group B = 2 --> 00000010
Group C = 3 --> 00000100

我们将这些值组合在一起,并将值(作为单个int)存储到页面中。例如。如果A和A组可以访问某个页面B,我们将值3(二进制为00000011)存储为页面访问控制。以同样的方式,我们将ORed组标识符的值与用户一起存储,以表示它们所在的组。

因此,要检查给定用户是否可以访问给定页面,您只需要将这些值组合在一起并检查该值是否为非零。这非常快,因为此检查在单个指令中实现,没有循环,没有数据库往返。

答案 6 :(得分:14)

当我有一堆布尔标志时,我喜欢将它们全部存储在一个int中。

我使用bitwise-AND将它们删除。例如:

int flags;
if (flags & 0x10) {
  // Turn this feature on.
}

if (flags & 0x08) {
  // Turn a second feature on.
}

答案 7 :(得分:11)

加密是所有按位操作。

答案 8 :(得分:10)

&安培; = AND:
掩盖特定位。
您正在定义应显示的特定位 或不显示。 0x0&amp; x将清除一个字节中的所有位,而0xFF不会更改x。 0x0F将显示低半字节中的位。

转换:
要将较短的变量转换为具有位标识的较长变量,必须调整位,因为int中的-1是0xFFFFFFFF而long中的-1是0xFFFFFFFFFFFFFFFF。保存 转换后应用掩码的身份。

| = OR
设置位。如果它们已经设置,则将独立设置这些位。许多数据结构(位域)具有IS_HSET = 0,IS_VSET = 1等标志,可以独立设置。 要设置标志,请应用IS_HSET | IS_VSET(在C和汇编中这非常方便阅读)

^ = XOR
找到相同或不同的位。

〜=不是 翻转位。

可以证明,这些操作可以实现所有可能的本地位操作。 因此,如果您愿意,可以仅通过位操作来实现ADD指令。

一些精彩的黑客:

http://www.ugcs.caltech.edu/~wnoise/base2.html
http://www.jjj.de/bitwizardry/bitwizardrypage.html

答案 9 :(得分:9)

您可以将它们用作哈希数据的快速而肮脏的方式。

int a = 1230123;
int b = 1234555;
int c = 5865683;
int hash = a ^ b ^ c;

答案 10 :(得分:8)

我在大约三分钟前使用了bitwise-XOR(^)来计算与PLC串行通信的校验和...

答案 11 :(得分:6)

按位&amp;用于屏蔽/提取字节的某个部分。

1个字节变量

 01110010
&00001111 Bitmask of 0x0F to find out the lower nibble
 --------
 00000010

特别是移位运算符(&lt;&gt;&gt;&gt;)通常用于计算。

答案 12 :(得分:6)

这是以字节格式从位图图像中读取颜色的示例

byte imagePixel = 0xCCDDEE; /* Image in RRGGBB format R=Red, G=Green, B=Blue */

//To only have red
byte redColour = imagePixel & 0xFF0000; /*Bitmasking with AND operator */

//Now, we only want red colour
redColour = (redColour >> 24) & 0xFF;  /* This now returns a red colour between 0x00 and 0xFF.

我希望这些小例子有帮助......

答案 13 :(得分:5)

在今天现代语言的抽象世界中,并不是太多。文件IO很容易浮现在脑海中,尽管它正在对已经实现的内容执行按位操作,并且没有实现使用按位操作的内容。尽管如此,作为一个简单的例子,此代码演示了在c#中删除文件的只读属性(以便它可以与指定FileMode.Create的新FileStream一起使用):

//Hidden files posses some extra attibutes that make the FileStream throw an exception
//even with FileMode.Create (if exists -> overwrite) so delete it and don't worry about it!
if(File.Exists(targetName))
{
    FileAttributes attributes = File.GetAttributes(targetName);

    if ((attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)
        File.SetAttributes(targetName, attributes & (~FileAttributes.ReadOnly));

    File.Delete(targetName);
}

就自定义实现而言,这是最近的一个例子: 我创建了一个“消息中心”,用于从我们的分布式应用程序的一个安装发送安全消息到另一个。基本上,它类似于电子邮件,包括收件箱,发件箱,已发送等,但它也保证了带有阅读回执的交付,因此除“收件箱”和“已发送”之外还有其他子文件夹。这就是我要求一般定义什么是“在收件箱中”或“在发送文件夹中”的内容。在已发送的文件夹中,我需要知道读取的内容和未读的内容。在未读的内容中,我需要知道收到的内容和收到的内容。我使用此信息构建一个动态where子句,用于过滤本地数据源并显示相应的信息。

以下是enum的组合方式:

    public enum MemoView :int
    {
        InboundMemos = 1,                   //     0000 0001
        InboundMemosForMyOrders = 3,        //     0000 0011
        SentMemosAll = 16,                  //     0001 0000
        SentMemosNotReceived = 48,          //     0011
        SentMemosReceivedNotRead = 80,      //     0101
        SentMemosRead = 144,                //     1001
        Outbox = 272,                       //0001 0001 0000
        OutBoxErrors = 784                  //0011 0001 0000
    }

你知道这是做什么的吗?通过使用“收件箱”枚举值(InboundMemos)安装(&amp;),我知道InboundMemosForMyOrders位于收件箱中。

这是该方法的简化版本,它构建并返回定义当前所选文件夹视图的过滤器:

    private string GetFilterForView(MemoView view, DefaultableBoolean readOnly)
    {
        string filter = string.Empty;
        if((view & MemoView.InboundMemos) == MemoView.InboundMemos)
        {
            filter = "<inbox filter conditions>";

            if((view & MemoView.InboundMemosForMyOrders) == MemoView.InboundMemosForMyOrders)
            {
                filter += "<my memo filter conditions>";
            }
        }
        else if((view & MemoView.SentMemosAll) == MemoView.SentMemosAll)
        {
            //all sent items have originating system = to local
            filter = "<memos leaving current system>";

            if((view & MemoView.Outbox) == MemoView.Outbox)
            {
                ...
            }
            else
            {
                //sent sub folders
                filter += "<all sent items>";

                if((view & MemoView.SentMemosNotReceived) == MemoView.SentMemosNotReceived)
                {
                    if((view & MemoView.SentMemosReceivedNotRead) == MemoView.SentMemosReceivedNotRead)
                    {
                        filter += "<not received and not read conditions>";
                    }
                    else
                        filter += "<received and not read conditions>";
                }
            }
        }

        return filter;
    }

非常简单,但在抽象级别上的整洁实现通常不需要按位操作。

答案 14 :(得分:4)

Base64编码就是一个例子。 Base64编码用于将二进制数据表示为可通过电子邮件系统发送的可打印字符(以及其他用途)。 Base64编码将一系列8位字节转换为6位字符查找索引。比特操作,移位和'或'或非'对于实现Base64编码和解码所需的比特操作非常有用。

这当然只是无数例子中的一个。

答案 15 :(得分:4)

我很惊讶没有人为互联网时代选择明显的答案。计算子网的有效网络地址。

http://www.topwebhosts.org/tools/netmask.php

答案 16 :(得分:3)

通常按位运算比进行乘法/除法更快。因此,如果您需要将变量x与9相乘,您将执行x<<3 + x,这比x*9快几个周期。如果此代码在ISR中,您将节省响应时间。

类似地,如果要将数组用作循环队列,则可以更快(更优雅)处理带有位操作的环绕检查。 (你的数组大小应该是2的幂)。例如:如果要插入/删除,可以使用tail = ((tail & MASK) + 1)代替tail = ((tail +1) < size) ? tail+1 : 0

此外,如果您希望错误标志一起保存多个错误代码,则每个位都可以包含一个单独的值。您可以将它与每个错误代码一起作为检查。这用于Unix错误代码。

n位位图也可以是非常酷且紧凑的数据结构。如果要分配大小为n的资源池,我们可以使用n位来表示当前状态。

答案 17 :(得分:3)

按位运算符对循环长度为2的幂的数组很有用。正如很多人提到的,按位运算符非常有用,用于标志图形,< strong>网络,加密。不仅如此,它们还非常快。我个人最喜欢的用途是循环 数组而不用条件。假设你有一个基于零索引的数组(例如,第一个元素的索引是0),你需要无限循环它。无限期地,我的意思是从第一个元素到最后一个元素并返回到第一个元素。实现这一目标的一种方法是:

int[] arr = new int[8];
int i = 0;
while (true) {
    print(arr[i]);
    i = i + 1;
    if (i >= arr.length) 
        i = 0;
}

这是最简单的方法,如果您想避免使用 if 语句,可以使用模数方法:

int[] arr = new int[8];
int i = 0;
while (true) {
    print(arr[i]);
    i = i + 1;
    i = i % arr.length;
}

这两种方法的缺点是模数运算符很昂贵,因为它在整数除法后寻找余数。第一种方法在每次迭代时运行 if 语句。使用按位运算符但是如果数组的长度是2的幂,则可以使用0 .. length - 1(按位和)运算符(如&)轻松生成类似i & length的序列。所以知道这一点,上面的代码变成

int[] arr = new int[8];
int i = 0;
while (true){
    print(arr[i]);
    i = i + 1;
    i = i & (arr.length - 1);
}

以下是它的工作原理。在二进制格式中,每个2的幂减去1的数字仅用1表示。例如,二进制中的3是11,7是111,15是1111,依此类推,你就明白了。现在,如果你&任何数字对一个只包含二进制数的数字会发生什么?让我们说我们这样做:

num & 7;

如果num小于或等于7,则结果将为num,因为每个位& - ed为1本身。如果num大于7,则在&操作期间,计算机将考虑7个前导零,当然在&操作后将保留为零,仅保留尾随部分。就像二进制9 & 7的情况一样,它看起来像

1001 & 0111

结果为0001,十进制为1,并对数组中的第二个元素进行寻址。

答案 18 :(得分:3)

似乎没人提到定点数学。

(是的,我老了,好吗?)

答案 19 :(得分:2)

它在sql关系模型中也很方便,假设您有以下表格:BlogEntry,BlogCategory

传统上,您可以使用BlogEntryCategory表在它们之间创建n-n关系 或者,当没有那么多的BlogCategory记录时,您可以使用BlogEntry中的一个值链接到多个BlogCategory记录,就像使用标记的枚举一样, 在大多数RDBMS中,还有一个非常快的运算符可以在“标记”列上进行选择...

答案 20 :(得分:2)

当你只想改变微控制器输出的某些位,但要写入的寄存器是一个字节时,你会做这样的事情(伪代码):

char newOut = OutRegister & 0b00011111 //clear 3 msb's
newOut = newOut | 0b10100000 //write '101' to the 3 msb's
OutRegister = newOut //Update Outputs

当然,许多微控制器允许您单独更改每个位...

答案 21 :(得分:2)

如果你想计算你的数字mod(%)的某个幂为2,你可以使用yourNumber & 2^N-1,在这种情况下与yourNumber % 2^N相同。

number % 16 = number & 15;
number % 128 = number & 127;

这可能仅仅是模数运算的替代方案,具有2 ^ N的非常大的红利......但即便如此,在我的.NET 2.0测试中,它对模数运算的速度提升可以忽略不计。我怀疑现代编译器已经执行了这样的优化。谁知道更多关于这个?

答案 22 :(得分:2)

我将它们用于多选选项,这样我只存储一个值而不是10个或更多

答案 23 :(得分:2)

数字x是2的幂吗? (例如,在计数器递增的算法中,并且只采用对数次的动作)

(x & (x - 1)) == 0

哪个是整数x的最高位? (例如,这可用于查找大于x)的最小功率2

x |= (x >>  1);
x |= (x >>  2);
x |= (x >>  4);
x |= (x >>  8);
x |= (x >> 16);
return x - (x >>> 1); // ">>>" is unsigned right shift

哪个是整数1的最低x位? (帮助找到被2整除的次数。)

x & -x

答案 24 :(得分:1)

我见过它们用于基于角色的访问控制系统。

答案 25 :(得分:1)

它们主要用于按位操作(惊喜)。以下是PHP代码库中的一些实际示例。

字符编码:

if (s <= 0 && (c & ~MBFL_WCSPLANE_MASK) == MBFL_WCSPLANE_KOI8R) {

数据结构:

ar_flags = other->ar_flags & ~SPL_ARRAY_INT_MASK;

数据库驱动程序:

dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);

编译器实现:

opline->extended_value = (opline->extended_value & ~ZEND_FETCH_CLASS_MASK) | ZEND_FETCH_CLASS_INTERFACE;

答案 26 :(得分:1)

Tower Of Hanoi线性解决方案使用按位运算来解决问题。

public static void linear(char start, char temp, char end, int discs)
{
    int from,to;
    for (int i = 1; i < (1 << discs); i++) {
        from = (i & i-1) % 3;
        to = ((i | i-1) + 1) % 3;
        System.out.println(from+" => "+to);
    }
}

可以找到此解决方案的解释here

答案 27 :(得分:1)

我认为这不算按位,但ruby的数组通过普通的整数位运算符定义了设置操作。所以[1,2,4] & [1,2,3] # => [1,2]。同样适用于a ^ b #=> set differencea | b #=> union

答案 28 :(得分:1)

我的问题在这里有一个真实的用途 -
Respond to only the first WM_KEYDOWN notification?

当在Windows C api中使用WM_KEYDOWN消息时,位30指定先前的键状态。如果在发送消息之前密钥已关闭,则值为1;如果密钥已启动,则该值为零

答案 29 :(得分:1)

每当我第一次开始C编程时,我理解了真值表以及所有这一切,但在我阅读本文http://www.gamedev.net/reference/articles/article1563.asp之前,并没有全部点击它如何实际使用它(这给出了真实的例子)

答案 30 :(得分:0)

我经常使用按位运算将选项组合存储在一个整数中。

int options = 0;

其中OPTION1可能定义为1,OPTION2定义为2,OPTION3定义为4,OPTION4定义为8,OPTION5定义为16,...

void addOption(int option)会使用|运算符为选项添加选项。

boolean hasOption(int option)会使用&运算符来测试选项中的选项。

答案 31 :(得分:0)

我在一些游戏开发书中看到它是一种更有效的乘法和分割方式。

2 << 3 == 2 * 8 
32 >> 4 == 32 / 16

答案 32 :(得分:0)

常用的是对齐,例如我需要在4字节或16字节边界上对齐数据。这在RISC处理器中非常常见,其中未对齐的加载/存储要么昂贵(因为它会触发异常处理程序然后需要修复未对齐的负载)或根本不允许。

对于2的幂的任何对齐,下一个对齐的pos可以如下计算:

aligned_offset = alignment + ((current_offset - 1) & ~(alignment - 1))

因此,在4字节对齐和9的当前偏移的情况下:

aligned_offset = 4 + ((9-1) & ~(4-1)) = 4 + (8 & 0xFFFFFFFC) = 4+ 8  = 12  

所以下一个4字节对齐的偏移量将是12

答案 33 :(得分:0)

还没有人提到过收藏品。有时候你有一些可能值的小集合,比如只有10或20个可能的值,你想把它们中的一些保留在一个集合中。当然,您可以使用常规Set实现,这很可能会使用支持哈希表。但由于可能的值集非常小,这实际上只是浪费时间和空间。相反,您可以将该集存储在单个intlong值中,这正是java EnumSet如果我没记错的话。

答案 34 :(得分:0)

一个非常具体的例子,但我用它们来让我的数独求解器运行得更快(我和朋友一起比赛)

每个列,行和3x3表示为无符号整数,并且当我设置数字时,我会在相关列,行和3x3平方中设置数字的适当位。

这样就可以很容易地看到我可以在给定方格中放置哪些可能的数字,因为我将右列,行和3x3方格一起“或”,然后不用这个给我留下代表可能的合法值的掩码对于给定的职位。

希望这是有道理的。

答案 35 :(得分:0)

我们使用Bitwise Flags使会话在我们内部网站上的登录权限更小。

答案 36 :(得分:0)

我用它们来实现快速BCD计算(会计师和审计员对fp舍入感到不安)。

答案 37 :(得分:0)

数据库世界中另一个真实世界的应用程序是MySQL,其数据类型为SET

按位运算符由DBMS存储SET数据类型。设置可以节省空间。

Element    SET Value    Decimal Value
Travel      00000001    1
Sports      00000010    2
Dancing    00000100    4
Fine Dining   00001000  8

答案 38 :(得分:0)

我总是假设按位操作是相当简单的操作,因此当运行时间至关重要时,通过位集实现的解决方案可以将运行时间提高一个恒定量,算法依赖。

答案 39 :(得分:0)

我写了一篇小维基文章,后来显示binary writer/reader。它在位级上工作,并显示如何使用按位运算符来打包数据。这可能是一个“真实世界”的例子,因为它在游戏中有应用。

答案 40 :(得分:0)

我将它们用作选项处理程序,例如在访问控制列表中描述特定资源。

看一下这篇文章http://planetozh.com/blog/2006/05/php-bitwise-operators-example-of-use/

编辑:

还有一个链接: http://blog.code-head.com/how-to-write-a-permission-system-using-bits-and-bitwise-operations-in-php

答案 41 :(得分:-3)

RAID 5/6 !!当您与本网站互动时,您可能正在使用它!是不是像 真实 那样!?