按位操作和用法


102

考虑以下代码:

x = 1        # 0001
x << 2       # Shift left 2 bits: 0100
# Result: 4

x | 2        # Bitwise OR: 0011
# Result: 3

x & 1        # Bitwise AND: 0001
# Result: 1

我可以用Python(和其他语言)理解算术运算符,但是我对“按位”运算符却不太了解。在上面的示例(来自Python书)中,我了解了左移功能,但不了解其他两个。

另外,按位运算符实际上是做什么用的?我会喜欢一些例子。



Answers:


163

按位运算符是对多位值进行运算的运算符,但从概念上讲一次只能运算一位。

  • AND仅当其两个输入均为1时,才为1;否则为0。
  • OR如果其输入之一或全部为1,则为1,否则为0。
  • XOR仅当其输入之一恰好为1时为1,否则为0。
  • NOT 仅当其输入为0时为1,否则为0。

这些通常可以最好地显示为真值表。输入的可能性在顶部和左侧,结果位是在输入交点处显示的四个值之一(如果不是,则为两个,因为它只有一个输入)。

AND | 0 1     OR | 0 1     XOR | 0 1    NOT | 0 1
----+-----    ---+----     ----+----    ----+----
 0  | 0 0      0 | 0 1       0 | 0 1        | 1 0
 1  | 0 1      1 | 1 1       1 | 1 0

一个示例是,如果您只想要整数的低4位,则将其与15(二进制1111)进行与,则:

    201: 1100 1001
AND  15: 0000 1111
------------------
 IS   9  0000 1001

在那种情况下,15中的零位有效地充当了过滤器,迫使结果中的位也为零。

此外,>><<通常作为按位运算符包含在内,它们分别将值“左”右移和左移一定数量的比特,丢掉将要移向的端点的比特,并在该比特处输入零。另一端。

因此,例如:

1001 0101 >> 2 gives 0010 0101
1111 1111 << 4 gives 1111 0000

请注意,Python中的左移是不寻常的,因为它没有使用固定的宽度来丢弃位-虽然许多语言根据数据类型使用固定的宽度,但是Python只是扩展宽度以迎合额外的位。为了获得Python中的丢弃行为,您可以按位向左移动,and例如在8位值中向左移动四位:

bits8 = (bits8 << 4) & 255

考虑到这一点,位运算符的另一个例子是,如果你有两个4位值要打包成一个8位的一个,你可以使用所有这三个操作员的(left-shiftandor):

packed_val = ((val1 & 15) << 4) | (val2 & 15)
  • & 15操作将确保两个值仅具有低4位。
  • << 4是一个4位左移位移动val1进入前的8位值的4位。
  • |简单地结合了这两者结合起来。

如果val1为7且val2为4:

                val1            val2
                ====            ====
 & 15 (and)   xxxx-0111       xxxx-0100  & 15
 << 4 (left)  0111-0000           |
                  |               |
                  +-------+-------+
                          |
| (or)                0111-0100

43

一种典型用法:

| 用于将某个位设置为1

& 用于测试或清除特定位

  • 设置一个位(其中n是位数,0是最低有效位):

    unsigned char a |= (1 << n);

  • 清除一点:

    unsigned char b &= ~(1 << n);

  • 切换一下:

    unsigned char c ^= (1 << n);

  • 测试一下:

    unsigned char e = d & (1 << n);

以您的清单为例:

x | 2用于将第1位的设置x为1

x & 1用于测试第0位x是1还是0


38

实际使用的按位运算符是什么?我会喜欢一些例子。

按位运算的最常见用途之一是解析十六进制颜色。

例如,这是一个Python函数,该函数接受类似于String的字符串,#FF09BE并返回其Red,Green和Blue值的元组。

def hexToRgb(value):
    # Convert string to hexadecimal number (base 16)
    num = (int(value.lstrip("#"), 16))

    # Shift 16 bits to the right, and then binary AND to obtain 8 bits representing red
    r = ((num >> 16) & 0xFF)

    # Shift 8 bits to the right, and then binary AND to obtain 8 bits representing green
    g = ((num >> 8) & 0xFF)

    # Simply binary AND to obtain 8 bits representing blue
    b = (num & 0xFF)
    return (r, g, b)

我知道有达到此目的的更有效方法,但是我相信这是一个非常简洁的示例,说明了移位和按位布尔运算。


14

我认为问题的第二部分:

另外,按位运算符实际上是做什么用的?我会喜欢一些例子。

仅得到部分解决。这是我的两分钱。

在处理许多应用程序时,编程语言中的按位运算起着基本作用。几乎所有低层计算都必须使用此类操作来完成。

在所有需要在两个节点之间发送数据的应用程序中,例如:

  • 计算机网络;

  • 电信应用(蜂窝电话,卫星通信等)。

在通信的较低层,数据通常以所谓的frame发送。帧只是通过物理通道发送的字节字符串。该帧通常包含实际数据以及一些其他字段(以字节为单位),这些字段是标头的一部分。标头通常包含字节,这些字节编码一些与通信状态有关的信息(例如,带有标志(位)),帧计数器,校正和错误检测代码等。要在帧中获取传输的数据并构建帧帧发送数据,则需要确定按位操作。

通常,在处理此类应用程序时,可以使用API​​,因此您不必处理所有这些细节。例如,所有现代编程语言都提供用于套接字连接的库,因此您实际上不需要构建TCP / IP通信框架。但是,请考虑为您编程这些API的优秀人员,他们肯定必须处理框架构造;使用各种按位运算来从低级通信到高级通信。

举一个具体的例子,假设有人给您一个文件,其中包含直接由电信硬件捕获的原始数据。在这种情况下,为了找到帧,您将需要读取文件中的原始字节,并通过逐位扫描数据来尝试找到某种同步字。在识别了同步字之后,您将需要获取实际的帧,并在必要时(并只是故事的开始)按Shift键以获取正在传输的实际数据。

另一个非常不同的低层应用程序系列是当您需要使用某些(较旧的)端口(例如并行端口和串行端口)来控制硬件时。通过设置一些字节来控制此端口,就指令而言,该字节的每个位对该端口具有特定含义(例如,请参见http://en.wikipedia.org/wiki/Parallel_port)。如果要构建可以对该硬件执行某些操作的软件,则将需要按位操作以将要执行的指令转换为端口可以理解的字节。

例如,如果您有一些物理按钮连接到并行端口以控制其他设备,则可以在软件应用程序中找到以下代码行:

read = ((read ^ 0x80) >> 4) & 0x0f; 

希望这能有所作为。


我会添加en.wikipedia.org/wiki/Bit_banging作为探索的另一条途径,特别是如果以并行和串行端口为例,说明按位操作可能有用。

6

我希望这可以澄清这两个问题:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

4
糟糕...试图成为西方最快的枪支。...最终成为一个白痴,甚至连两个都不知道二进制文件:(修正了它
。– Amarghosh

1
x & 1没有很好地说明这种效果x & 2
dansalmo 2013年

5

将0视为假,将1视为真。然后按位的and(&)和or(|)就像常规的and和或或一样工作,只不过它们一次完成值中的所有位。通常,如果您可以设置30个选项(例如,在窗口上绘制样式),而又不想传递30个单独的布尔值来设置或取消设置每个布尔值,则可以将它们用作标志。将选项合并为一个值,然后使用&检查是否设置了每个选项。OpenGL大量使用这种标志传递方式。由于每个位都是一个单独的标志,因此您将获得以2(即仅设置了一位的数字)为幂的标志值1(2 ^ 0)2(2 ^ 1)4(2 ^ 2)8(2 ^ 3) 2的幂可以告诉您如果标志打开则将哪个位置1。

还要注意2 = 10,所以x | 2是110(6)而不是111(7)如果没有位重叠(在这种情况下为true)| 就像加法一样。


5

我没有看到上面提到的内容,但是您还会看到一些人使用左右移位进行算术运算。左移x等于乘以2 ^ x(只要它不会溢出),右移等同于除以2 ^ x。

最近,我看到人们使用x << 1和x >> 1来加倍和减半,尽管我不确定他们是否只是想变得聪明,还是真的比普通运算符有明显的优势。


1
我不了解python,但是在像C或更低级别的汇编语言中,按位移位的效率要高得多。要了解两者之间的区别,您可以使用C编写一种以各种方式执行此程序的程序,然后仅编译为汇编代码即可(或者,如果您知道汇编语言,则早已知道这一点:))。请参见说明数量的差异。
2013年

2
我反对使用位移运算符的论据是,大多数现代编译器可能已经在优化算术运算,因此,聪明之处或多或少是不利于编译器的。我没有C,编译器或CPU设计方面的专业知识,因此请假定我是正确的。:)
P. Stallworth

这应该更高。我不得不以这种方式处理一些使用按位运算符的代码,而这个答案帮助我弄清楚了事情。
菲利普·奥格

4

套装

可以使用数学运算来组合集合。

  • 联合运算符|将两个集合组合在一起,形成一个新集合,其中两个集合都包含项。
  • 交集运算符&仅在两个项中都获得项。
  • 差异运算符-在第一组中获得项目,但在第二组中则没有。
  • 对称差运算符^获取任一集合中的项目,但不能同时获取两者。

自己尝试:

first = {1, 2, 3, 4, 5, 6}
second = {4, 5, 6, 7, 8, 9}

print(first | second)

print(first & second)

print(first - second)

print(second - first)

print(first ^ second)

结果:

{1, 2, 3, 4, 5, 6, 7, 8, 9}

{4, 5, 6}

{1, 2, 3}

{8, 9, 7}

{1, 2, 3, 7, 8, 9}

这个答案与问题完全无关,似乎已经从其他地方复制并粘贴了。
doctaphred

这个问题问“实际使用的按位运算符是什么?”。这个答案提供了一个鲜为人知但非常有用的按位运算符用法。
泰庚

3

本示例将向您显示所有四个2位值的操作:

10 | 12

1010 #decimal 10
1100 #decimal 12

1110 #result = 14

10 & 12

1010 #decimal 10
1100 #decimal 12

1000 #result = 8

这是用法的一个示例:

x = raw_input('Enter a number:')
print 'x is %s.' % ('even', 'odd')[x&1]

2

另一个常见用例是操纵/测试文件权限。请参阅Python stat模块:http : //docs.python.org/library/stat.html

例如,要将文件的权限与所需的权限集进行比较,可以执行以下操作:

import os
import stat

#Get the actual mode of a file
mode = os.stat('file.txt').st_mode

#File should be a regular file, readable and writable by its owner
#Each permission value has a single 'on' bit.  Use bitwise or to combine 
#them.
desired_mode = stat.S_IFREG|stat.S_IRUSR|stat.S_IWUSR

#check for exact match:
mode == desired_mode
#check for at least one bit matching:
bool(mode & desired_mode)
#check for at least one bit 'on' in one, and not in the other:
bool(mode ^ desired_mode)
#check that all bits from desired_mode are set in mode, but I don't care about 
# other bits.
not bool((mode^desired_mode)&desired_mode)

我将结果转换为布尔值,因为我只关心真实性或虚假性,但是打印每个值的bin()值将是值得进行的练习。


1
在上一个示例中,您错了。这里看起来应该是这样的: not bool((mode ^ desired_mode) & 0777)。或者(更容易理解): not (mode & 0777) ^ desired_mode == 0。AND将仅保留感兴趣的位,XOR将检查所有所需的位均已设置。显式== 0比较比有意义bool()
Vadim Fint 2012年

我不认为这是特定于文件操作的。例如,在PyQt中,您对做类似的事情setWindowFlags。范例:setWindowFlags(SplashScreen | WindowStaysOnTopHint)。我仍然感到困惑,因为您似乎将切换设置为“打开”,因此在这种情况下,“和”似乎更直观。
eric 2014年

2

在科学计算中,整数的位表示形式经常用于表示真假信息的数组,因为按位运算比迭代布尔数组快得多。(高级语言可能会使用位数组的概念。)

一个很好且相当简单的例子是Nim游戏的一般解决方案。看一下Wikipedia页面Python代码。它大量使用按位异或。^


1

可能有更好的方法来查找数组元素在两个值之间的位置,但是如本示例所示,在此处有效,而and则无效。

import numpy as np
a=np.array([1.2, 2.3, 3.4])
np.where((a>2) and (a<3))      
#Result: Value Error
np.where((a>2) & (a<3))
#Result: (array([1]),)

1

我没有看到它,本示例将为您显示2位值的(-)十进制运算:AB(仅当A包含B时)

当我们在程序中持有表示位的动词时,需要执行此操作。有时我们需要添加位(如上),有时我们需要删除位(如果动词包含)

111 #decimal 7
-
100 #decimal 4
--------------
011 #decimal 3

使用python: 7&〜4 = 3(从7删除代表4的位)

001 #decimal 1
-
100 #decimal 4
--------------
001 #decimal 1

使用python: 1&〜4 = 1(从1删除代表4的位-在这种情况下1不是“包含” 4)。


0

操纵整数的位很有用,但对于网络协议(可能一直指定到位)通常是有用的,但是可能需要操纵更长的字节序列(不容易转换为一个整数)。在这种情况下,使用允许对数据按位进行操作的位库很有用-例如,可以将字符串“ ABCDEFGHIJKLMNOPQ”作为字符串或十六进制导入并对其进行位移(或执行其他按位操作):

>>> import bitstring
>>> bitstring.BitArray(bytes='ABCDEFGHIJKLMNOPQ') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')
>>> bitstring.BitArray(hex='0x4142434445464748494a4b4c4d4e4f5051') << 4
BitArray('0x142434445464748494a4b4c4d4e4f50510')


0

要翻转位(即1的补码/取反),可以执行以下操作:

由于ExORed与全1的值会导致取反,因此对于给定的位宽,您可以使用ExOR对其进行取反。

In Binary
a=1010 --> this is 0xA or decimal 10
then 
c = 1111 ^ a = 0101 --> this is 0xF or decimal 15
-----------------
In Python
a=10
b=15
c = a ^ b --> 0101
print(bin(c)) # gives '0b101'
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.