Python基础之位运算符(含原码反码补码的通俗解释)

目录

1 二进制
2 原码、反码、补码
3 位运算符
4 位运算符使用技巧


上回学习运算符时,漏了位运算符,因为位运算符理解起来稍微有点复杂,所以要单独写一篇~

要理解按位运算符,要先了解计算机进行存储和计算的底层逻辑。

因此我们从最基础的二进制说起。


1 二进制

只要学过计算机,就不可能不知道二进制。

我们知道,十进制是逢十进一,譬如11,左边的1在十位上,代表10,右边的1在个位上,就是1。

把1502这个数字拆开看,就是有1个1000,5个100,0个10,2个1, 1502=1*10^3+5*10^2+0*10^1+2*10^0,也就是说,十进制中的位数对应的就是10的幂,个位是0次幂,十位是1次幂,百位是2次幂,以此类推……

同理,二进制中的位数对应的就是2的幂,那么对于二进制下的1010,转化成十进制下的数,就是1*2^3+0*2^2+1*2^1+0*2^0=8+2=10

用2进制数数,首先是0,然后是1,接下去是10,而不是2,因为二进制中只有0和1。

小白可以练习一下从0写到10,写完对一下结果:

image


2 原码、反码、补码

这三个码的产生,都和表示减法(负数)有关,他们的正数表示完全一样

至于为什么为了表示个负数,出现了三个码,我们一个一个来说。

2.1 原码

日常生活中我们用负号(减号)解决了负数的表示问题,但在计算机中怎么加上这个负号呢?人们就想了个办法,用最高位存放符号,正数为0, 负数为1

以4位二进值数为例,最高位是符号位,那么后面只有三位来表示数字。例如,0001表示1,要表示-1,就把最高位写成1,得到1001。

当用8位来表示一个整数时,从右往左数的第8位即为符号位,当用16位来表示一个整数时,从右往左数的第16位即为符号位。我为了少写点数字>.<,本文举例都用4位。

原码

这种方法简单直观,但在减法运算中有问题。计算1减去1,就是0001和1001加起来,会得到1010,这是咋了?1加-1,等于-2?

因此原码无法进行减法运算。


2.2 反码

正数的反码和正数的原码完全一样,对负数原码的非符号位取反,就得到负数的反码(其实也就是将正数的反码统统取反),譬如+1的反码是0001,-1的反码就是1110。

反码

此时我们计算+1和-1相加,即0001+1110=1111,正好是-0,相反数相加等于0,没有问题。

但此时一个0有了两种表示法,0000和1111,一个数两种表示,有点奇怪。

除此之外,虽然相反数相加没有问题,但是其他数的减法依旧不对劲,譬如0010+1110,等于10000,最高位1溢出,就是0000,所以2-1=0???

所以,这个码还是不行。


2.3 补码

正数的补码和正数的原码完全一样,负数的补码等于负数的反码+1。但是,反码加1并不是补码的真正来历,只不过补码恰好等于反码加1,这么计算更加方便而已。

补码

关于补码的本质和定义,其实初看难以理解,但是仔细想想,会发现这就是自然而然、浑然天成的东西。

我很喜欢一个词,“十方圆满”,一直以来我的微信签名都是这四个字。那么,这个词讲的是怎样一种状态呢?

  • 譬如,我们原地转个圈,就可以回到原点。
  • 譬如,我们在地球上,一直往东走可以到达的地方,一直往西走,也可以到达。
  • 譬如,我们把时钟的时针往前拨180度,和往后拨180度,得到的结果是一样的。
  • 再譬如,爱因斯坦说,如果人能看到无限远,那么他就能看到自己的后脑勺。

我到底在说什么呢?

对于四位二进制数,最大只能存放4位,就只有0000-1111这么大的空间,就只用2^4=16种排列组合的方式,空间是有限的。那么,这个空间圆不圆满呢?我们想办法让它从线性变成圆就好了,理解它,就像是理解24:00就是00:00,360°就是0°一样。

先不管负数,假设我们有一条绳子,上面从左到右依次写着0、1、2…15、16,就像这样。


image

我们把绳子首尾相连,也就是把写着0和16的两端拧到一起,圈成一个圆。

image

这个圆上只有16个数字,16就等于0,这是为了方便后面和四位二进制数的16种排列组合相对应。

我们从0出发,顺时针走1个单位,得到1,逆时针走1个单位,得到15,1+15=16。同样的,顺时针走7个单位得到7,逆时针走7个单位得到9,7+9=16。这个16有个专业的叫法,叫做

这里的模是什么意思呢?简单举几个例子:

  • 24小时制下,24就是模。
  • 转一圈360°,360就是模。
  • 表上有12个刻度,12就是模。


    image

现在看这个圆,从0开始顺时针转,数值越来越大,最后到15,再转一个单位,就又回到0,没有什么问题吧?

好,我们开始引入负数,并且把顺时针看成加法,把逆时针看成减法,这下会得到什么呢?

顺时针走1个单位,认为是加1,所以得到1,圆的右边还是1~7。逆时针走1个单位,就是减1,得到-1,同理,把圆的左边都填满,得到下图。

image
  • 首先,距离0相同距离的数,相加等于0,这解决了相反数相加为0的问题;
  • 其次,这个圆可以把减法转化成加法,a-b=c,其实等同于a+(16-b)=c,因为模是16。可以自己验证下,譬如1-2=-1(逆时针走2个单位),在这里就等价与1+14=-1(顺时针走14个单位)。

完美啊!接下来,我们把这个圆上的十进制数字,替换成二进制就好了。

image

问题来了,正数的二进制码毫无疑问,负数的二进制码要怎么推出来呢?还有,1对面那个问号应该是什么数字?在7和-7之间,应该是8呢,还是-8呢?

先不管那个问号是什么数,7的二进制码是0111,再加1,得到1000,问号处的二进制码应该是1000,再加1,就是10001,以此类推,我们就能补满整个圆上的码。

image

补完你会发现,-1本就该是1111啊!因为1111再加1,为10000,但因为四位,最高位的1溢出了,所以就得到0000,-1加1可不就是0吗!

相应的,我们可以验证-4加4,即1100+0100,等于10000,溢出位不算,0000啊!这种表示法下,所有的相反数相加都是0000~

再看看别的减法呢,譬如6-3,即0110+1101,等于10011,即0011,就是3~哈哈,都通过验证了呢!

现在就剩最后一个问题了,就是0的对面应该是什么?从7出发,加1应该是8,但是从-7出发,减1应该是-8,这个1000到底代表哪个数?

其实不难发现,圆的右边都是正数,最高位皆为0,左边都是负数,最高位皆为1,这有点像原码中人为定义的最高位是符号位,所以1000自然而然应该是-8。

虽然求补码的过程中没有特意留出一个符号位,但最终得到的补码却可以用最高位来判断正负。补码的符号位就是这么来的。

image

比比赖赖这么多,其实求补码没那么麻烦,可以汇总成一句话:正数补码不变,求负数补码用模减去其绝对值即可。

前面我们说过模是16,那么求a-b,其实等同于a+(16-b),所以求-b这个负数的补码,用16减b不就行了吗?比如说,求-2,就用16的二进制码减去2的二进制码。可是,四位二进制码的空间里,根本没有16这个数啊?没有就对了,因为它是模,也就是10000,在八位中16的表示是00010000。

那么,我们计算-2的补码,其实就转化成:
10000
-0010
=1110


2.4 小结

总结一下:

  • 原码:将最高位作为符号位(0表示正,1表示负)。
  • 反码:如果是正数,则和原码一样;如果是负数,符号位为1,其余各位取反。
  • 补码:如果是正数,则和原码一样;如果是负数,将反码加上1。
image

很多文章在解释补码时,都是原码→反码→补码这样的思路。

先介绍最简单的原码,它方便人读数,但无法做减法,接着引申出反码,它是原码过渡补码的中间产物,但无法解决0的问题,最后引出补码,反码直接加1即可得到补码,这个码可以完美解决前面两个码的问题。但实际上我们也知道了补码的发展过程并不如此,之所以提供这样的思路,只是为了便于计算补码。

关于补码的定义和本质,我解释得挺业余的,举的例子也不够严谨,主要是为了方便自己理解和记忆。要看专业的解释,就得去找权威书籍或教材来看了。


3 位运算符

计算机底层在存储数据的时候,都是用补码存储,位运算符就是基于补码进行的计算,包括:

  1. 位逻辑运算符: 与&,或|,异或^,取反~。
  2. 位移运算符:左移<< ,右移>> 。
位运算符 名称 算法
& 按位与 两个二进制数相应位都为1,则该位的结果为1,否则为0
l 按位或 两个二进制数相应位有一个为1时,结果位就为1
^ 按位异或 两个二进制数相应位不同时,结果为1
~ 按位取反 对二进制进行取反,即 1 取反为 0 ,0 取反为 1
<< 按位左移 将二进制数左移n位,相当于乘以2的n次方
>> 按位右移 将二进制数右移n位,相当于除以2的n次方,如果不能整除,则向下取整
a = 2
b = 3
print("a和b转换为二进制为:", bin(a), bin(b))
-------------------------------------
[output]: a和b转换为二进制为: 0b10 0b11

下面我就用2和3,也就是0010和0011举例。

a = 0010  #2
b = 0011  #3
 
a&b = 0010  #2
a|b = 0011  #3
a^b = 0001  #1
~a = 1101  #-3

a<<1 = 0100 #左移一位,相当于乘2,得到4
a>>1 = 0001 #右移一位,相当于除以2,得到1
a>>2 = 0000 #右移两位,相当于除以4,不能整除时向下取整,得到0


4 位运算符使用技巧

在日常工作中,用到位运算符的场景似乎不多,它能用来做什么呢?

4.1 按位与

通常,我们写程序判断奇偶数,是除以2看余数。现在可以用该数和1进行按位与,结果是1,就是奇数,是0,则为偶数。

def Odd_Even(x):
    if x&1 == 1:
        print(x,'是奇数')
    else:
        print(x,'是偶数')

Odd_Even(666)
---------------------
[output]: '666 是偶数'

4.2 按位或

任意数和1按位或,可以向上求最接近的奇数。

6|1
---------------------
[output]: 7

7|1
---------------------
[output]: 7

4.3 按位异或

一个数a,另一个数b进行两次异或运算,最后结果不变,即(a ^ b) ^ b = a。

5^7
---------------------
[output]: 2

5^7^7
---------------------
[output]: 5

因此用异或运算调换两个数字的值。

a = 5
b = 7
a = a^b
b = b^a
a = a^b
print(a,b)
---------------------
[output]: 7 5

当然Python中其实可以用一行代码就完成交换。

a = 5
b = 7
a,b = b,a
print(a,b)
---------------------
[output]: 7 5

简单的加密也可以用异或运算,比如实际密码是password,既怕忘了又怕直接写下来被别人看到,就可以用一个简单的key作为密钥,两者作异或运算,得到tip,把这个tip记到小本子里。忘记密码时,将key和tip做异或运算,就能得到原密码啦~

password = 587645
key = 111111
tip = password ^ key
print(tip)
---------------------
[output]: 607610

tip ^ key
---------------------
[output]: 587645

4.4 按位取反

对一个数按位取反,等于它的相反数减1。

~55
---------------------
[output]: -56

对一个数两次取反,结果不变。

~~55
---------------------
[output]: 55

4.5 按位左移

a左移b位,就是把a转为二进制后左移b位,后面缺位补0,相当于a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。

5<<2
---------------------
[output]: 20

4.6 按位右移

a右移b位,就是把a转为二进制后右移b位,前面缺位补0,相当于a除以2的b次方,并向下取整。

14>>2
---------------------
[output]: 3

计算机中的数是用二进制来表示的,因此位运算可以更直接、更高效地实现运算操作。对于乘2除2,二进制左右位移一下就搞定,速度非常快,所以尽量用位移来代替代码中的乘除。

最后注意一点,在Python中只能对整数进行位运算~


文中图片的水印网址为本人CSDN博客地址:BeSimple

参考链接:
1)原码、反码、补码的产生、应用以及优缺点有哪些? - 张天行的回答 - 知乎
2)原码,反码,补码的深入理解与原理
3)Python位运算用途以及用法
4)js 中位运算的应用
5)位运算简介及实用技巧(一):基础篇

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容