按位运算和使用

时间:2009-11-17 04:37:06

标签: python binary operators bit-manipulation

考虑以下代码:

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书),我理解左移而不是其他两个。

另外,实际使用的是按位运算符?我很欣赏一些例子。

17 个答案:

答案 0 :(得分:146)

按位运算符是处理多位值的运算符,但概念上一次一位。

  • AND仅在其输入的两个为1时才为1,否则为0。
  • 如果其输入中的一个或两个为1,则
  • OR为1,否则为0。
  • XOR仅在其输入的一个为1时才为1,否则为0。
  • NOT仅在输入为0时为1,否则为0。

这些通常最好显示为真值表。输入可能性位于顶部和左侧,结果位是输入交叉点处显示的四个中的一个(在NOT的情况下为两个,因为它只有一个输入)。

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位值向左移位4位:

bits8 = (bits8 << 4) & 255

考虑到这一点,另一个按位运算符示例是,如果要将两个4位值打包成8位值,则可以使用所有三个运算符(left-shift,{ {1}}和and):

or
  • packed_val = ((val1 & 15) << 4) | (val2 & 15) 操作将确保两个值只有较低的4位。
  • & 15是一个4位移位,用于将<< 4移动到8位值的前4位。
  • val1只是将这两者结合在一起。

如果|为7且val1为4:

val2

答案 1 :(得分:40)

一种典型用法:

|用于将某个位设置为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用于将x的第1位设置为1

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

答案 2 :(得分:36)

  

实际使用的按位运算符是什么?我很欣赏一些例子。

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

例如,这是一个Python函数,它接受类似#FF09BE的字符串,并返回其红色,绿色和蓝色值的元组。

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)

我知道有更有效的方法来实现这一点,但我相信这是一个非常简洁的例子,说明了移位和按位布尔运算。

答案 3 :(得分:9)

我认为问题的第二部分是:

  

另外,实际使用的是按位运算符?我很欣赏一些例子。

只是部分解决了。这是我的两分钱。

编程语言中的按位运算在处理大量应用程序时起着至关重要的作用。几乎所有的低级计算都必须使用这种操作来完成。

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

  • 计算机网络;

  • 电信应用(手机,卫星通信等)。

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

通常,在处理这类应用程序时,API可用,因此您无需处理所有这些细节。例如,所有现代编程语言都为套接字连接提供了库,因此您实际上不需要构建TCP / IP通信帧。但想想那些为你编写这些API的好人,他们肯定要处理框架结构;使用各种按位运算从低级别到高级别的通信来回。

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

另一个非常不同的低级别应用程序系列是当您需要使用某些(某种古老的)端口(例如并行和串行端口)来控制硬件时。通过设置一些字节来控制这些端口,并且该字节的每个位在指令方面对该端口具有特定含义(例如参见http://en.wikipedia.org/wiki/Parallel_port)。如果您想构建使用该硬件执行某些操作的软件,则需要按位操作将要执行的指令转换为端口所理解的字节。

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

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

希望这有所贡献。

答案 4 :(得分:6)

我希望这澄清了这两点:

x | 2

0001 //x
0010 //2

0011 //result = 3

x & 1

0001 //x
0001 //1

0001 //result = 1

答案 5 :(得分:5)

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

另请注意2 = 10所以x | 2是110(6)而不是111(7)如果没有比特重叠(在这种情况下是真的)|像添加一样。

答案 6 :(得分:5)

我没有看到上面提到的,但你也会看到有些人使用左右移位进行算术运算。左移x乘以2 ^ x(只要它不溢出),右移相当于除以2 ^ x。

最近我见过人们使用x&lt;&lt; 1和x>&gt;尽管我不确定他们是否只是想要变得聪明,或者是否真的比普通的操作员有明显的优势。

答案 7 :(得分: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]

答案 8 :(得分:2)

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

一个很好的,相当简单的例子就是Nim游戏的一般解决方案。请查看Python上的the Wikipedia page代码。它大量使用按位异或,^

答案 9 :(得分: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()值是值得的。

答案 10 :(得分:2)

设定

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

  • union运算符|将两个集合组合成一个包含其中任何一个项目的新集合。
  • 交叉运算符&仅在两者中获取项目。
  • 差异运算符-获取第一组中的项目但不获取第二组中的项目。
  • 对称差异运算符^可以在任一集中获取项目,但不能同时获取两者。

自己动手:

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}

答案 11 :(得分:1)

可能有更好的方法来查找数组元素在两个值之间的位置,但正如此示例所示, &amp; 在这里工作,而 没有。

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]),)

答案 12 :(得分:1)

我没有看到它提到,这个例子将显示2位值的( - )十进制操作:A-B(仅当A包含B)

当我们在程序中保存表示位的动词时,需要此操作。有时我们需要添加位(如上所述),有时我们需要删除位(如果动词包含那么)

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

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

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

使用python: 1&amp; 〜4 = 1(从1中删除代表4的位 - 在这种情况下1不是'包含'4)..

答案 13 :(得分:0)

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

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

答案 14 :(得分:0)

以下按位运算符:&|^~logic gates影响的方式返回值(基于其输入)信号。您可以使用它们来模拟电路。

答案 15 :(得分:0)

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

由于将值与所有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'

答案 16 :(得分:0)

您可以使用位掩码将二进制转换为十进制;

int a = 1 << 7;
int c = 55;
for(int i = 0; i < 8; i++){
    System.out.print((a & c) >> 7);     
    c = c << 1;
}

这是 8 位数字,您也可以做更多。