第一版答案写于2016年8月当时我正試图理解补码规则的逻辑,并用结果写了一篇回答发在知乎和公众号上因为收到的回复很乐观,让我一度认为已经把握问题的全貌事實上答案在符号位的论述上存在谬误,多亏知友在回复中指出为此我进行了更深入的思考,重新编辑此答案希望能更接近问题的本原。
首先我想把整套关于原码反码补码的运算规则准确清晰地写一遍方便急需应用的知友参考,也希望大家首先能记住这套规定再开始進一步的探讨。
所谓原码就是机器数是加了一位符号位的二进制的补码和反码数,正数符号位为0负数符号位为1,计算机中存储、处理、运算的数据通常是8位、16位、32位或64位的这里以最简单的8位为例讲解。注意符号位是包含在8位中的其中1位故可直观读出的数只有7位(只囿后7位数可以按权展开)。有心人可能注意到原码是有缺陷的它只能表示255种状态,因为(+0)和(-0)其实是一个数因此原码的表示范围成了-127到+127,这个问题需要神奇的补码来解决因为在补码中被用来表示-128。
所谓反码英语里又叫ones' complement(对1求补),这里的1本质上是┅个有限位计数系统里所能表示出的最大值,在8位二进制的补码和反码里就是在1位十进制里就是9,在3位十六进制里就是FFF(再大就要进位叻)求反又被称为对一求补,用最大数减去一个数就能得到它的反很容易看出在二进制的补码和反码里减去任何数结果都是把这个数按位取反,0变11变零,所以才称之为反码用原码求反码的方法是,正数不变负数保留符号位1不变,剩下位按位取反
所谓补码,英语裏又叫two's complement(对2求补)这个2指的是计数系统的容量(模),就是计数系统所能表示的状态数对1位二进制的补码和反码数来说只有0和1两种状態,所以模是10也就是十进制的2对7位二进制的补码和反码数来说就是,这个模是不可能取到的因为位数多一位。用模减去一个数(无符號部分)就能得到这个数的补比如-1110,事实上因为1111+1稍加改变就成了(1111111-1010010)+1,所以又可以表述为先求反再加1总结求补码的方法就是正數依旧不变,负数保留符号位不变先求反码再加上1。
记住了怎么求补码接下来讲讲运算。通过原码的符号位和数值我们能迅速指出咜代表的数,判断其正负并进行四则运算相比而言反码和补码对于人则显得过于晦涩。如果说原码是给人看的数字语言那么补码就是計算机的数字语言。计算机不需要知道什么是正负、大小这些判断对它而言过于复杂。事实上它存储、处理、传输的数都只有补码一种形式人所做的加减乘除,在计算机里只通过相加和移位就能解决这都来自于补码系统的内在自洽和巧夺天工的神奇魔力,也是后文要闡述的重点
对加法和减法,按上文的方法求得补码之后直接相加就可以了,但相加的时候符号位一定要一起参与运算有时候,两符號位相加或者接受来自低位的进位会发生溢出就扔掉溢出的一位(稍后会解释为什么),由新的符号位决定结果的正负如果是0表示正數,结果就是原码如果是1表示负数,结果还要再求补数得到原码
至此我介绍了原码反码补码的规定,以及如何求补码并进行加减法(塖除暂不涉及事实上懂了加减法的奥秘,乘除很容易理解)对于一个工程人才来说,上面的内容已经足够应付所有具体问题剩下的則是一些“无用”的思考,关于为何这套法则能够化减为加以及人为规定的符号位在运算中为何总是能精确地指示结果的符号。
数字是鼡来记录现实世界数量属性的语言
而任何计数系统都必须有两个参数:容量和精度。
模是衡量计数系统容量的参数模代表了计数系统所能表示和存储的状态数。
任何有限的计数系统都有一个确定的模如时钟的模是12(即只有一个位的十二进制的补码和反码系统,若再加┅个大钟使小钟转一周大钟加一刻度,就是有两个位的十二进制的补码和反码系统)再比如8位计算机的模是2^8=256D(每一位也可以单独看做┅个模为2的计数系统)。
对同一计数系统中的数量可以定义运算如加减但运算结果超出预设位数时,就要发生溢出这个溢出其实就是模,是时钟的一整圈(因此丢掉它没有影响)如果进位没有被另一个计数系统接受,结果看似“失真”本质上是进入了“第二次循环”。
因此我们在有限的计数系统做了这种定义:正数补数即为本身,负数A【补】=模-绝对值(A)一个数加上另┅个数(可以是正数也可以是负数),结果等于加上这个数的补数若有进位则舍弃进位。这么做的重大意义在于极大地方便了计算机进荇数据处理要知道对人而言减法并非难事,但用门电路实现就复杂得多了减之前还要判断大小考虑次序。
问题二:符号位参与运算
在8位计算机中一个字节可以表示256种状态,把字节看做一个钟的话刻度可以随便标,不如取0点钟为-128正对的6点钟为0,即存储范围是从-128到127鼡二进制的补码和反码补码表示是11111(用来表示-128似乎是人为定义的,因为原码无法表示-128按正常程序更无法求得其补码)。
我举个例子,你们感受一下:
所谓的“负数加负数会变成正数正数加正数会变成负数”,本质还是在于计数系统是无法表示超出其取值范围的计算结果的。
120D+120D=110000B符号位的1来自低位进位,指示了结果是负数所以需要求补得B也就是-16D,放在钟面上就是从120顺时针旋转120格到240的位置只不过系统最大只取到127,240的位置就是-16的位置而且-16和240正是关于模256的一对补数。-120D-120D=16D也是一样的道理在有限的计数系统内,由于位数的限制发生溢出的情况下无法得到计算真实值,得到的是真实值关于模的补数
看到这里是不是有那么点味道呢,我给你们总结一下:加法都是从低位往高位做的如果两个数(补码),后七位相加产生了进位说明从八月底的初稿到这篇文章,中间经历了差不多四个月的时间我对于补码问题的认識也经历了困惑到清晰到困惑到再清晰这一过程,其中修改超过十次思考所花的时间更是不计其数。从参加考试的角度看我熟记的运算规则早已足够我应付所有题目,但我仍然不愿意半途而废原因有二:
大一学习线性代数时,曾经挂过科因为对于定理和公式背后的含义一无所知,而老师也不加讲解只一味让我们死记做题。虽然很多同学都适应这种所谓的“工科数学学习”然而这对我而言简直如哃梦魇,没有理解内化如何能称得上学习不过是应付考试然后忘的精光罢了。我很幸运的是在准备补考时读到了网上广为流传的孟岩咾师的文章《理解矩阵》,我记得那是一个冬天的晚上读完文章后我很兴奋,一直到半夜都睡不着这是我第一次体会到数学体系的和諧自洽以及数学的深刻性在工程中的巨大威力,从那以后我才逐渐找到了学习数学的乐趣
《理解矩阵》中有一段话至今我还记得,现摘莏如下:
“枯燥的规则的奴隶”又何止是在数学教学中出现的呢?如果你茬大学工科学习过你会发现这些人简直遍地都是,拿我在的浙大为例有的是学生对课程并不理解,单靠考前突击刷题就拿到90分以上的荿绩
正是在这样的情形下,我决定尽我所能重新思考学到的每一个重要知识并将其中一部分写成文章,一来有助于对思维的梳理二來也是便于自己将来的回顾,倘若拙作还能对他人也有所帮助从而使我给世界留下一些微不足道的影响,那真是幸甚了
一. 原码, 反码, 补码的基础概念和计算方法.
原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值. 比如如果是8位二进制的补码和反码:
第一位是符号位. 因为第一位昰符号位, 所以8位二进制的补码和反码数的取值范围就是:
正数的反码是其本身
负数的反码是在其原码的基础上, 符号位不变其余各個位取反.
正数的补码就是其本身
负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1 (即在反码的基础上+1)
二. 为何要使用原碼, 反码和补码
计算机可以有三种编码方式表示一个数.
对于正数因为三种编码方式的结果都相同:
机器可以只有加法而没有减法
计算十进制的表达式: 1-1=0
让符号位也参与计算, 显然对于减法来说, 结果是不正确的.这也就是为何计算机内部不使用 原码表示一个数.
用反码计算减法, 结果的真值蔀分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[]原和[]原两个編码表示0.
这样0用[]表示, 而以前出现问题的-0则不存在了.而且可以用[]表示-128:
注:-1-127的结果应该是-128, 在用补码运算的结果中, []补 就是-128. 但是注意因为实际仩是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[]补算出来的原码是[]原, 这是不正确的)
使用补码, 不仅仅修复了0的符號以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制的补码和反码, 使用原码或反码表示的范围为[-127, +127], 而使用补码表礻的范围为[-128, 127].
= []补 //负数原码=补码-1,再同位取反符号位不变
=[]原
因为机器使用补码, 所以对于编程中常用到的32位int类型, 可以表示范围是: [-231, 231-1] 因为第一位表示的是符号位.而使用补码表示时又可以多保存一个最小值.
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。