Skip to content

Latest commit

 

History

History
329 lines (255 loc) · 11.6 KB

Bitwise-Operators.md

File metadata and controls

329 lines (255 loc) · 11.6 KB

Intorduction to Bitwise Operators

1. Binary Representation

1.1 Just a Little BIT

现在是一个入门级的关于Python二进制运算(bitwise operations)的介绍

刚接触二进制运算会觉得他有点深奥和奇怪,但是你很快就能上手使用他们了。

二进制运算(bitwise operations),是直接针对 位元(bits -- from Binary Digits) 进行操作的运算。在所有的电脑中,数都是用二进制的位来表示的,也就是一系列的0s和1s。实际上,不只如此,电脑中的所有东西都是用bits来表示的。本节内容将介绍最基本的二进制运算并且说明他们能用来做些什么。

二进制运算常使编程的初学者感到困惑,所以如果开始有些疑问和困难也可不必担心。虽然你确实不需要在日常变成中见到二进制运算,但很多时候他们确实很有用,这时候你应该对这些有个基本的了解。

例子

先来看下面的例子,这里展示了6个基本的二进制运算,以后在逐个进行解释。

print 5 >> 4  # Right Shift
print 5 << 1  # Left Shift
print 8 & 5   # Bitwise AND
print 9 | 4   # Bitwise OR
print 12 ^ 42 # Bitwise XOR
print ~88     # Bitwise NOT

1.2 Lesson 10: The Base 2 Number System

当我们计数时,通常我们用的是以10为基的系统,即十进制。这意味着数的每一位可以是0~9这10个数字。而在二进制中,我们以2为基来计数,数的每一位只能是0或1这两个值。计数的方法与十进制相同,唯一的区别在于,当某一位数大于1时要向前进一位(而十进制则是某一位大于9时向前进位)

例如,数字1和0在十进制与二进制中是相同的。但是在二进制中,一旦计数到2比1大了,这就导致需要用"10"来表示,再增加1则变成了"11" (3) ,再增加1结果为"100" (4)。

十进制中每个浮点位置是10的指数次方,与之相反,二进制的每个数位是2的指数次方。即最右边的一位是 1位/个位(1's bit) 2的0次方,旁边是 2位(2's bit) 2的1次方,在接下去就是 4, 8, 16, 32 ... 位

二进制数 '1010' 就是十进制数10在二进制中的表示,因为可以看出 8位(8's bit) 和 2位(2's bit) 的状态为“开(open)”:

8's bit  4's bit  2's bit  1's bit
    1       0       1      0 
    8   +   0    +  2   +  0  = 10 

在Python中,可以通过在二进制数前加 0b 符号,实现用二进制表示普通的数。

例子

下面是一些例子,试图理解这些输出

print 0b1,    #1
print 0b10,   #2
print 0b11,   #3
print 0b100,  #4
print 0b101,  #5
print 0b110,  #6
print 0b111   #7
print "******"
print 0b1 + 0b11
print 0b11 * 0b11

1.3 I Can Count to 1100

有了上面的基础,就可以尝试练习使用二进制来计数了。

先自己把4~12的二进制数写出来,而不要借助于 str() 方法或其他的外部函数。

练习

填写4~12的二进制数,使用 0bxxxx 格式

one = 0b1
two = 0b10
three = 0b11
four = 0b100
five = 0b101
six = 0b110
seven = 0b111
eight = 0b1000
nine = 0b1001
ten = 0b1010
eleven = 0b1011
twelve = 0b1100

1.4 The bin() Function

理解二进制运算的最大障碍就是如何在二进制中计数。学习了上面的内容之后,下面的部分将会轻松一些。

Python提供了函数帮助我们来完成二进制的运算。为了显示一个数对应的二进制数可以使用 bin() 函数。该函数以一个整数作为输入参数,之后以字符串类型返回其二进制的值。(记住,使用了 bin 函数之后你就不能再将其按照数来操作了)。

同理可以按照八进制或十六进制表示数,相应的函数为 oct()hex() (目前先不学习这一部分内容)

练习

下面的代码中已经提供了一个使用 bin 函数的例子,继续用 printbin() 显示输出2~5的的二进制数

print bin(1)
for n in range(2,6):
    print bin(n)

1.5 int()'s Second Perameter

之前已经使用过Python中的 int() 函数,他可以将非整数的数转化为整数,如:

int("42")
# ==> 42

但是 int 函数还有一个可选的第二参数。如果所给的字符串包含了一个数和一个该数对应的基,则函数会返回该数对应的十进制数。所以,如果运行 int("1010", 2) 会返回 10 因为,0b1010是10的二进制数。

练习

已经给出了几种 int 函数中第二参数的使用方法,在第六行,使用 int 函数输出二进制数 11001001 对应的十进制数。

print int("1",2)
print int("10",2)
print int("111",2)
print int("0b100",2)
print int(bin(5),2)
print int("11001001",2)

2. The Bitewise Operators

2.1 Slide to the Left! Slide to the Right!

接下来要介绍的两个操作符是左移和右移运算符(left and right shift bitwise operators)。这两个操作符可以把一个数的位按指定的数量移,先用例子来说明。

下面的代码块展示了位层面的操作。注意,括号中的内容,移动的数量总是正整数:

Left Bit Shift (<<)  
0b000001 << 2 = 0b000100 (1 << 2 = 4)
0b000101 << 3 = 0b101000 (5 << 3 = 40)       

Right Bit Shift (>>)
0b0010100 >> 3 = 0b000010 (20 >> 3 = 2)
0b0000010 >> 2 = 0b000000 (2 >> 2 = 0) 

这个操作符在数学上等价于,每移动一位,数字就除以或乘以2之后做了floor运算取整(这主要针对奇数除不尽的情况),但通常最简单的理解还是认为是吧所有的1和0向左或向右移动指定的位数。

值得注意的还有,位操作只有对整数才有意义。对浮点数和字符串作上述位操作会产生无意义的结果。

练习

将给出的变量 shift_rigth 做右移2位的移动,将 shift_left 做左移2位的位操作,运行之前先思考一下得到的结果是多少。

shift_right = 0b1100
shift_left = 0b1

# Your code here!
shift_right = shift_right >> 2
shift_left = shift_left << 2
print bin(shift_right)
print bin(shift_left)

2.2 A BIT of This AND That

二进制的与(AND/&)操作,在二进制数位的层面对比两个数,返回对应数位都为真的位上是1其余为0的数。先看一个例子:

     a:   00101010   42
     b:   00001111   15       
 =======================
 a & b:   00001010   10

可以看出,a和b中只有2位和8位都是1,所以,a&b 的运算只有这两个位数上是1。注意,使用 & 符号得到的结果小于等于min(a,b)。

记住,对于a和b的每一位都要进行如下的逻辑判断:

0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1

因此:

 0b111 (7) & 0b1010 (10) = 0b10

等于2.

练习

作为练习,用 & 运算符求 0b11100b101 的与运算,并用 print 输出其二进制数的字符串, 输出结果前先估计一下答案。

print bin(0b1110 & 0b101)

2.3 A BIT of This OR That

二进制的或(OR/|)操作,类似的,在二进制数位的层面对比两个数,当对应位有一个或两个1时为1,否则该位为0。例如:

    a:  00101010  42
    b:  00001111  15       
    ================
a | b:  00101111  47

同样,使用 | 符号得到的结果会大于等于max(a,b)。

对于给定数位,比较时遵循如下的关系:

0 | 0 = 0
0 | 1 = 1 
1 | 0 = 1
1 | 1 = 1

练习

做一个和上面类似的练习,用 print 输出 0b11100b101 或运算 ^ 的二进制字符串,还是先自己心算一下。

print bin(0b1110 | 0b101)  # 0b1111 (15)

2.4 This XOR That

这是二进制的异或运算(XOR/^),同样在二进制数位层次比较两个数,当两个数对应的数位 仅有一个 为1时,返回1(两个1会返回0)。比如:

    a:  00101010   42
    b:  00001111   15       
    ================
a ^ b:  00100101   37

相同数的异或(XOR)运算,总是得到0.

练习

print 输出 0b11100b101 异或运算 ^ 的二进制字符串,还是先自己心算一下。

print bin(0b1110 ^ 0b101)  # 0b1011 (11)

2.5 See? This is Not That Hard

最后来看二进制的否(NOT/~)操作,反转给定数的二进制数的每一位。这对于电脑的意义是十分复杂的这里暂且不表。只需要知道,在数学上,这个操作等价于给数字加1之后加负号。

以上我们就学习了所有的二进制操作符。下一课介绍他们的用法,这里看一个关于否操作的例子.

例子

print ~1
print ~2
print ~3
print ~42
print ~123

3. A Bit More Complicated

3.1 The Man Behind the Bit Mask

bit mask 是一個可以帮助你进行位操作的变量。可以用来指定打开某一位而关掉其他所有数位,或者返回一个整数对应的二进制数的为1和0的数位的信息。

例如,我们现在希望看看数 a 的二进制数位,右边数起第三位是否是 的状态,即该位是否为1。使用 & 运算符和 mask 变量配合,将是完成上述判断的最佳办法,具体代码如下:

mask = 0b100
desired = a & mask

使用 &0b100 将会使 a 的除右数第三位之外的所有数位为 。这时,如果右数第三位为0则返回0,否则返回1,反过来讲,若结果大于0则指定的数位为 ,否则为

练习

写一个函数,check_bit4,该函数以一个整数作为输入,检查该数的右起第四位是否为 。如果判断为 则返回字符串 "on" 否则返回 "off"

def check_bit4(a):
    mask = 0b1000
    result = a & mask
    if result > 0:
        return "on"
    else:
        return "off"

3.2 Turn It On

也可以用mask变量和 | 将一个数对应二进制数位的某一位打 。例如,我希望最右边的一位为 ,那么可以做如下操作:

a = 0b110 # 6
mask = 0b1 # 1
desired =  a | mask # 0b111, or 7

使用二进制的与操作 | 对于指定的数位,可以打开原来为 的状态,或保持原来为 的状态仍然为开。

练习

已经给出了一个变量,a。使用一个 bitmask 和 一个值,确保 a 右起第三位为开。记住用 printbin() 函数将结果输出到屏幕。

a = 0b10111011
mask = 0b100
desired =  a | mask
print bin(desired)

3.3 Just Flip Out

用异或 (XOR/^) 符号可以有效进行的反转所有的数位的操作,比如希望反转 a 中所有的数位,那么只需要下面的操作:

a = 0b110 # 6
mask = 0b111 # 7
desired =  a ^ mask # 0b1

练习

给定了一个8位的变量 a。用一个bitmask和值,反转a的所有数位。用 printbin() 函数将结果输出到屏幕。

a = 0b11101110
mask = 0b11111111
desired =  a ^ mask
print bin(desired)

3.4 Slip and Slide

最后,你还可以配合左移(<<)和右移(>>)操作,将mask移动到指定的位置。

例如,我希望将整数 a 从右数起第10位打开,我们可以将1从第1位移动到第10位,而不必直接输入整个(10位长的)数字,如下:

a = 0b101 
mask = (0b1 << 9)  # One less than ten 
desired = a ^ mask

注意:要从第1位移动到第10位只需移动9次!

练习

首先,我们定义一个函数 flip_bit ,接受输入 (number, n) 两个参数,将 number 的第 n 位反转,返回反转后的结果。返回的结果为二进制字符串bin()

def flip_bit(number, n):
    mask = (0b1 << (n-1))  # One less than n
    desired = number ^ mask
    return bin(desired)