二进制——负整数

原码
我们在初中时学的正负号,它们长得和加减号一样。比如:+5 表示正5,正号可以省略。负5写成-5, 负号不可以省略。

计算机中,一切数据都是用0和1表示, 正负号也是如此,0表示正号,1表示负号。

一个整数(int)是4个字节,也就是有32位,当仅考虑正整数时,32位都用来表示绝对值的大小,但现在我们需要考虑正负,所以,很自然的,我们要占用一个位来表示正负号。这样,就只剩下31位来表达数的绝对值大小。并且,就像-5中的负号写在最左边一样,计算机采用最左边的那位——专业一点称为:最高位——来表示正负号,重复一下:0表示正号,1表示负号。

(作业:请思考,为什么要用0,而不是1来表示正号?)

如果仅仅根据以上的知识,我们先可以想一下整数中,+1 和 -1 如何用二进制表示(纯粹为了不眼晕,这次我每个字节间加一个空隔):

00000000 00000000 00000000 00000001 (+1)

10000000 00000000 00000000 00000001 (-1)

区别就是最高位,1个是0,表示正号,1个是1,表示负号……除了这32位实在太长了些以外,一切都很直观嘛!红色的0表示正号,红色的1表示负号。这就是二进制的原码的表示法。

有关计算机正负表达的问题,似乎就这样搞定了,传说中的二进制也没有什么复杂的嘛……且慢!请回想一下,我们为什么要采用二进制?原因在于机器“喜欢”读二进制。所以在最高位用0或1来表示正负的方法,对人倒是直观,但机器却有怨言。

首先来看,0 这个数如何表达?代数老师教育我们,0是个两面派,+0 和 -0 都是0。当用原码来表达0时,这一点也得到直观的体现,请看:

00000000 00000000 00000000 00000000 (+0)

10000000 00000000 00000000 00000000 (-0)

计算机最喜欢的事是“确定”的事,最不喜欢的是“不确定”的事。所以,在“机器语言”这个层面上,对一个数有两种表达方法,这是不可接受的。

这里我们要抬杠一下,说:机器又不是人,它没有感情,我们就强制这样规定不就得了?

真正不能接受的,是运行效率。一个数字有两种表达方法,会造成计算机在判断一个数是不是0时,需要判断两次。

另一个问题就更严重了——在说这个严重问题之前,我们先复习一下小学算术课的竖式加法,请注意,时光倒流,我们回到小学的十进制年代:

123

234 +

----------

347

没有哪位看不懂这个竖式吧?下面我们会遇上好几次这样的竖式,重要的变化是除非特别说明,否则其中加数1、加数2以及和,都是二进制的。并且为了大家计算方便,需要时,我们会在二进制后面,添加一对括号(),其内写上对应的十进制数,或者更详细的说明。

现在来说那个严重问题。假设有+1和-1两个数,计算机如果要相加这两个数,结果将不正确:

00000000 00000000 00000000 00000001 (+1)

10000000 00000000 00000000 00000001 (-1) +

——————————————————----

10000000 00000000 00000000 00000010 (-2)

算出的错误结果是 -2。显然,采用“原码”的话,简单的将各位相加并不能得出正确的结果。相反,必须将用于表示正负号的最高位,和其它位分别处理。由于计算机可处理的最小内存单位是字节这一,对单独一位进行处理,又是一件不利于性能的事情。

事情如何解决,如果是让我来处理,恐怕我没有答案,还好当初的计算机先辈都是数学专家。他们想到了用“补码”来表达负数的这样一个“神奇的”方法。

反码和补码
反码和补码,都仅对负整数而言。对于正整数,我们坚持用原码表示,或才可以认为,正整数的原码、反码和补码,都一个样(就是原码)。

什么叫补码?我们得先从“反码”说起。所谓的反码,就是指对于在负数的原码的基础上,除去最高位以外,其它位全部取反,而取反的意思,就是如果原来是1,变成0;原来是0,则变成1的过程。

假设有一个数,-1,用原码表示,则为:

原码:10000000 00000000 00000000 00000001

反码:11111111 11111111 11111111 11111110

反码中,红色的1是保持不变的符号位(负号);蓝色的1或0,则是原码中对应位的取反所得。这就是-1的反码表示法。但这并不是我们要的最终结果。我们还需要进一步将反补变换为补码。

再强调一次:正整数的反码表示法不需要进行转换,它等同于原码。现在我们来考虑,如果采用反码,正负零的表示法分别是什么:

00000000 00000000 00000000 00000000 (+0的反码,没变,还是原码)

11111111 11111111 11111111 11111111 (-0的反码,全是1)

结论是用反码表达,0仍然有两种表示法,并且看上去差别更大了……,因此我们说了,反码只是临时产物,最终我们要的补码。

这个神秘的补码如何表达呢?其实很简单,有个公式:补码 = 反码 + 1。

当然,我们仍然要强调一下,对于正整数,补码就是原码。所以+0的补码仍然是0000 0000 0000 0000。而-0呢?它等它的反码加1,所以就是:

11111111 11111111 11111111 11111111 (负0的反码)

00000000 00000000 00000000 00000001 (正1) +

-----------------------------------------------------

00000000 00000000 00000000 00000000 (0)

可能你一下子有些反应不过来?为什么结果全是0了呢?我们说过了,二进制的世界里,数字只有0和1,因此相加时,肯定是逢2进1。如果你对“逢2进1”这个词有些似懂非懂,让我们想想十进制里的“逢10进1吧”,想像999 + 1 这个算式,写在竖式是:

999

1 +

-------------------

1000

按照这个规则,你再算一下上面的那个竖式:

11111111 11111111 11111111 11111111 (负0的反码)

00000000 00000000 00000000 00000001 (正1) +

-----------------------------------------------------

100000000 00000000 00000000 00000000 (最前面有个1啊?)

最前面,不是有一个1吗?是有个1,但是这个1不在实际运行结果中,因为一个int占用4个字节,最多只能放得下32位我们所看到那个“最高位”的1,已经是超出32位了。就像一个水杯,最多只能放1升水,你再往里倒,只能“溢出”——啊,没错,请记住“溢出”这个计算机术语!

小时候,小伙伴间经常玩这种游戏:你说1,他就说2,你说999,他就说1000,你说1万,他就说1万零1……反正无论你说个多大的数,他就说一个比你大1的数,真是气倒我们……今天,我们学习了二进制,这种游戏可以有个结束了!计算机中的数据是要占用内存资源的。对于一个整数,当你说出“11111111 11111111 11111111 11111111”这个数时,对方就只能闭嘴了,因为若是在这个数上再加1,结果将是:0。


看看我们扯出一大堆话,稍稍回归一下,用补码表示时,正负零是不是一致的?正0自然是32个0,而负0呢?如前面所罗嗦的,它通过负0的反码,再加上1,得到补码,正好也是32个0。

第一个问题解决了,用补码表示时,0的正负数表达方法得到的结果一致!

(作业:请写出以下几个十进制整数的补码:-1、5、-5、-12、-302)

第二个问题,+1 加上 -1,运算过程是否简捷,结果是否正确。

00000000 00000000 00000000 00000001 (+1,补码就是原码)

11111111 11111111 11111111 11111111 (负1的补码) +

------------------------------------------------------------

00000000 00000000 00000000 00000000 (0)

运算过程就是简单把逐位相加,进位,运算结果:(+1) + (-1) = 0。完全正确!原来,第一个问题和第二个问题用到的竖式,是那么的相像。a+b 等于 b+a,加法交换率,我们很小就学会了。不过在这里,它们各有自己的代表的意义。

另外,我们也发现,原来在计算机整数运算中,并不需要特别的减法,仅需把减法换成对减数取负后的加法即可。比如9 - 8的运算过程,就是9 + (-8)的过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值