qakcn
学生会会长
学生会会长
  • 注册日期2008-10-31
  • 最后登录2021-01-05
  • 生日1988-8-18
  • 光玉3394颗
阅读:1304回复:3

科普第28篇——计算机是怎样处理负数的

楼主#
更多 发布于:2012-08-09 03:39
115
前两篇我们讲了计算机做加减法的一些原理,但是都是针对的是正整数,如果计算中出现负数就没法处理了。

在研究如何计算负数加减法之前,我们首先需要一种方式来表示负数。

二进制既然能表示两种状态,何不利用它来表示正负呢?
完全可以,比如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位二进制数的表示范围了
0111 1111
127
……
……
0000 0000
0
1111 1111
-1
……
……
1000 0000
-128


16位数也是差不多:
0111 1111 1111 1111
32767
……
……
0000 0000 0000 0000
0
1111 1111 1111 1111
-1
……
……
1000 0000 0000 0000
-32768


甚至,可以更一般性一点,对于n位二进制数:
011......11
2n-1-1
……
……
000......00
0
111......11
-1
……
……
100......00
-2n-1


此外,还有如下的规律
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——月食
喜欢0 评分0
幻魔皇
学生会干部
学生会干部
  • 注册日期2008-12-12
  • 最后登录2020-02-24
  • 生日1991-3-31
  • 光玉1626颗
沙发#
发布于:2012-08-09 08:18
不是很明白,但是好像很厉害
回复(0) 喜欢(0)     评分
iejr
光坂大学生
光坂大学生
  • 注册日期2012-01-14
  • 最后登录2016-02-06
  • 生日1992-3-4
  • 光玉1560颗
2楼#
发布于:2012-08-09 09:01
。。。唉,當年學大計機的時候就不懂,現在看了一遍還是有點暈的說。。。
回复(0) 喜欢(0)     评分
双月的骑士
学生会干部
学生会干部
  • 注册日期2012-02-12
  • 最后登录2023-04-25
  • 生日1994-9-5
  • 光玉5089颗
3楼#
发布于:2012-08-11 13:59
看着头都晕了
恐惧并不是胆小,而是明白自身的弱小!
2013年春季新番动画一览
回复(0) 喜欢(0)     评分
游客

返回顶部