阅读:1461回复:3
科普第28篇——计算机是怎样处理负数的
前两篇我们讲了计算机做加减法的一些原理,但是都是针对的是正整数,如果计算中出现负数就没法处理了。
在研究如何计算负数加减法之前,我们首先需要一种方式来表示负数。 二进制既然能表示两种状态,何不利用它来表示正负呢? 完全可以,比如8位二进制数(还记得吗,8位二进制数是1字节),我们可以用最高位的0来表示正数,最高位1来表示负数(为什么要这么定呢?其实反过来也完全可以,不过大家都习惯这样规定,成为标准了)。这样表示实际数字的部分就只有7位了。 比如: 0101 0110就是+101 0110,也就是十进制的86 1101 0110就是-101 0110,也就是十进制的-86 可以看到,除了符号位之外,其他7位都是使用原码来表示的。 但是问题出现了,如果我们要处理负数的加减法,我们还得先把符号位去掉,然后还要比较两个数的大小才能决定最终结果的符号。而且,要命的是0有两种表示方法!0000 0000和1000 0000都可以表示0! 也许有人想到了,上次说减法的时候提到的反码,我们可以用反码表示负数。 比如: 0101 0110就是+101 0110,也就是十进制的86 1010 1001就是-101 0110,也就是十进制的-86 这样处理正数和负数的加法倒是方便了,但是处理负数和负数的加减法还是很麻烦,还是要单独处理这个符号位。而且,糟糕的是0还有用两种表示方法:0000 0000和1111 1111。 有没有一种方法可以方便地处理符号位呢?单独处理符号位实在是太蛋疼了。 最好能把符号位也看作数的一部分,直接用现有的加法器就对数进行计算。可能吗? 还真可能! 这就是我们将要说到的补码。 补码很简单,就是用2n减去负数的绝对值(n为二进制数的位数)。比如除去1位符号位之后的7位二进制数,就是用27(二进制数1000 0000)减去负数的绝对值,在加上符号位。补码又被称作2的补数。 比如: -110 1010,就用1000 0000 - 110 1010 = 001 0110,加上符号位就是1001 0110 -010 1100,就用1000 0000 - 010 1100 = 101 0100,加上符号位就是1101 0100 因为1000 0000 = 111 1111 + 1,所以补码可以用负数绝对值反码加1来计算,而且可以顺便把符号位也取反,就不用上面的最后一步了(加上符号位)。 比如: -0110 1010,绝对值的反码是1001 0101,加1就是1001 0110 -0010 1100,绝对值的反码是1101 0011,加1就是1101 0100 这样,0的表示方法就只有0000 0000,1000 0000表示的是十进制-128,1111 1111表示的是十进制-1。 这样,我们可以列出8位二进制数的表示范围了
16位数也是差不多:
甚至,可以更一般性一点,对于n位二进制数:
此外,还有如下的规律 1、0的补码还是0 2、最小的负数(-2n-1)的补码还是它本身 3、其他数字的补码就是它的相反数,如1111 1111(十进制-1)的补码是0000 0001(十进制1),0010 1011(十进制43)的补码是1101 0101(十进制-43) 4、增加表示数字的位数时,只要把增加的高位用符号位填充就行了。比如8位数0010 0000扩展为16位数时,只要把前面的位数都用0填充就行了(0000 0000 0010 0000),负数也是一样的(比如8位数1101 1010扩展为16位1111 1111 1101 1010) 负数的表示方法现在是有了,那么计算呢? 前面说到了,补码的目的就是为了能把符号位当作数的一部分计算,那么怎么做呢? 我们分为几种情况举例(以8位二进制数为例): 1、正数加正数 较小的正数加正数和我们前面提到的方法是一样的: 如:十进制 85 + 13 = 98 0101 0101 + 0000 1101 = 0110 0010 但是我们能表示的最大正数是127,所以如果计算结果大于127就叫做溢出。 如:十进制 85 + 86 = 171 0101 0101 + 0101 0110 = 1010 1011 1010 1011表示的是-85,而两个正数相加是不可能为负的,所以从符号位我们就可以判断是否溢出了。 2、正数加负数 这里就要体现出我们使用补码的方便之处了。处理正数加负数时,我们只要像处理一般数字相加那样计算就行了。 正数绝对值大于负数,可知结果为正: 如:十进制 85 + ( -13 ) = 72 0101 0101 + 1111 0011 = 1 0100 1000 出现了进位,但是只要我们把进位舍弃,就是最终的结果0100 1000(十进制72)了。 正数绝对值小于负数,可知结果为负: 如:十进制 -85 + 13 = -72 1010 1011 + 0000 1101 = 1011 1000 而1011 1000正是-72。 所以,正数和负数相加,直接把进位舍去就是计算的结果了。正数加负数不可能出现溢出,想想为什么? 3、负数加负数 和正数类似,负数相加也会出现溢出,也就是说结果小于-128就算溢出了。 我们先来看看不溢出的情况: 如:十进制 -85 + (-13) = -98 1010 1011 + 1111 0011 = 1 1001 1110 我们可以发现,和正数的计算类似,舍弃进位就是我们要的结果1001 1110(十进制-98)了。 那溢出呢? 如:十进制 -85 + (-86) = -171 1010 1011 + 1010 1100 = 1 0101 0111 可以看到,舍去进位之后的结果是0101 0111(十进制87),和正数相加溢出类似,从符号位我们就可以判断是否溢出了。 4、关于0的计算 涉及到0的计算处理很简单,把0当作一个正数就可以了。 而唯一可能出现结果为0的加法计算,只有正数加负数时,正数绝对值等于负数绝对值的时候。而得到的结果和正数绝对值大于负数绝对值的处理一样,把进位舍去就行了。 如:十进制 85 + (-85) = 0 0101 0101 + 1010 1011 = 1 0000 0000 舍去进位之后就是结果0000 0000(十进制0)。 6、减法 为什么减法我们不单独讨论了呢?因为我们有一个著名的定义:“减去一个数,等于加上一个数的相反数”。 而这里又体现出补码的方便之处了。前面我们说过,除最小负数之外的任何数的补码就是他的相反数。 所以减法我们可以非常方便地转换为上面提到的加法来计算,只要求减数的补码,然后做加法就可以了。 正数减正数,等于正数加负数 正数减负数,等于正数加正数 负数减正数,等于负数加负数 负数减负数,等于负数加正数 另外我们可以发现,当被减数与减数符号相同时(转换为加法时符号不同),不会出现溢出。 当被减数与减数符号不同时(转化为加法符号相同),如果结果的符号与被减数不同,则发生溢出。 7、关于-128的计算 因为我们讨论的是8位二进制数,所以是-128,如果是16位数就是-32768了,处理方法是类似的。 十进制的-128在做加法时和其他负数并没有区别。而在做减法时就有个问题,它的补码还是他本身。 我们的处理方法就是,不去处理! 因为一个数减去-128,也就是相当于加上128。 如果是正数加上128的话,那结果绝对是溢出(128就已经大于能表示的最大正数127了),而-128补数是他本身也不影响溢出判断。 比如:十进制 13 - (-128) = 13 + 128 = 141 0000 1101 - 1000 0000 等于 0000 1101 + 1000 0000 = 1000 1101 结果是溢出的。和前面提到的减法溢出判断是吻合的(当被减数与减数符号不同时,如果结果的符号与被减数不同,则发生溢出)。 如果是负数加上128的话,会产生进位,但是舍去进位之后结果是正确的。 如:十进制 -13 - (-128) = -13 + 128 = 115 1111 0011 - 1000 0000 等于 1111 0011 + 1000 0000 = 1 0111 0011 舍去进位之后是正确结果0111 0011(十进制115) 总结: 我们可以把以上提到补码的计算规律总结一下: 加法时把包括符号位的数字直接相加,结果舍去进位。 当两个加数的符号相同时(均为正或均为负),如果结果的符号与两个数都不同,则发生溢出。 当两个加数的符号不同时,不会出现溢出。 减法时把减数计算补码,然后再与被减数相加,结果舍去进位。 当被减数与减数符号相同时,不会出现溢出。 当被减数与减数符号不同时,如果结果的符号与被减数不同,则发生溢出。 可以看到,当用补码来表示整数时,加减法的计算和溢出的判断就变得非常简单。连特殊的数字(0和最小负数)都符合上面的规律,根本不用特殊处理。 所以,虽然今天我们的标题是怎样处理负数,但其实我们把计算机整数加减法的所有问题都解决了。即使是上一篇提到的把16位数拆成两段8位数来计算的方法也同样适用。 那么,接下来我们要解决的就是乘除法了。 ==========之前的文章========== 科普第1篇——计算机色彩 科普第1篇补遗——CSS颜色 科普第2篇——光盘 科普第3篇——2、8、10、16 科普第4篇——电池 科普第5篇——浏览器 科普第6篇——字符编码 科普第7篇——加密解密 科普第8篇——移动通信技术 科普第9篇——为什么32位CPU不能支持大于4GB内存? 科普第10篇——智能手机简介 科普第11篇——14.52-14.49=0.0299999? 科普第12篇——为什么HTTPS会更安全? 科普第13篇——计算机语言 科普第14篇——字体(上) 科普第15篇——字体(中) 科普第16篇——字体(下) 科普第17篇——域名解析 科普第17篇补遗——域名 科普第18篇——虚拟内存 科普第19篇——时间 科普第20篇——日期 科普第21篇——日期时间补遗及计算机的时间 科普第22篇——网络(0):基本概念 科普第23篇——二进制的线性编码 科普第24篇——网络(1):OSI模型 科普第25篇——外部接口(第一次修订) 科普第26篇——计算机是怎样做加法的 科普第27篇——计算机是怎样做加减法的 科普番外篇1——虽然没用但了解一下也很有趣的知识 科普番外篇2——月食 |
|||||||||||||||||||||||||||||||||||||
沙发#
发布于:2012-08-09 08:18
![]() |
|
2楼#
发布于:2012-08-09 09:01
。。。唉,當年學大計機的時候就不懂,現在看了一遍還是有點暈的說。。。
|
|
|
3楼#
发布于:2012-08-11 13:59
看着头都晕了
|
|
|