organic

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

1. 问题

我们知道,在计算机中是用补码来存放实际的正负数的,那么计算机为什么要用补码来存放数据,计算机课本中,那个补码 = 反码 + 1的公式又是怎么来的?

要了解这些问题的答案,我们先要了解一些基本的知识,最基本的同余公式这里就不讲了,看本帖之前需要去离散数学或数论中了解下基本的同余运算即可。

 

2. 计算机的字长和同余计算

计算机的字长是指计算机运算时,能存放的最大数字的的位长,如我们常说的32位机,字长就是32位的,寄存器只有32bit,最多能放32个1,能表达的最大数字就是232 - 1

当超过这个数字后,由于无法存储,会被计算机丢弃,同时会设置CPU的溢出标志位,这就是所谓的溢出

为了简单,我们用8位机来做演示,即最大能表示的数字是28 - 1

m = 28

所以,计算机的计算 (a + b)时,实际上计算的是 (a + b) % m (mod m),超过m的部分都会被丢弃,只取余数的部分,即计算机中的计算实际都是一个模m的同余计算

如 1000 0000 + 1000 0001 = 1 0000 0001

由于最高位超出最大能够表示的数,多出的位将溢出丢弃,所以最后的结果为 0000 0001 = 1

 

3. 补码的定义和计算

设m > b >= 0,b为整数

则b的补 b = m – b (注意,这里定义的是补,而不是补码,不要弄混了)

由于有正负数,计算机中对正负数的补码进行了区分定义:

1> 正整数b的补码为自身,即b的补码仍为b

2> 负整数-b的补码为b的补,即b

我们知道,a - b即可转化为 a + (-b),如果用补码来运算

a – b = a + (-b)  (mod m) 在计算机中计算的是 a + b  (mod m)

那么他们的同余吗(即他们模m后的值相等吗)?

b = m – b

∴ a + b = a + (m – b)

          ≡ a – b (mod m)

∴ 将负数转化为补码进行计算后,仍然是成立的

所以,计算机中用补码来储存所有的数后,就不需要增加减法器了,用加法器就可以代替计算减法,这样能节省电路设计,当然,这还只是其中的一个好处之一

 

4. 负数的表示和运算

前面讲了计算机中用补码来存储数字,我们知道,在8位机中,-1的补码为 1111 1111 = 255,那么在8位机中,这个数字即可以表示是-1的补码,也可以表示是255的补码,这就产生一个冲突了。

为了解决这种冲突,计算机中,从可用的8位中挪用了一个最高位来区分正负数,即当最高位为1时,该数为负数,为0则表示正数

这样,由于最高位被挪用,8位机在表示正负数时,就只有7位有效数字了,设m2 = 27 = 1000 0000 = 28 / 2 = m/2

正负数仍用低7位的补码来表示,此时的模要换成m2了。

如-1,1模m2的补码为 (m2-1) = 0111 1111,然后将最高位置为1,最终在计算机中表示为: 1111 1111(即oxFF)

image

image

 

这时,我们前面定义的补码还有用吗?运算的法则还需要改变吗?

1> a + b

当 a + b 小于 m2时,不会向符号位进位,所以结果就是 a + b,没问题

当2个正整数相加大于或等于m2时,就会向最高位进位,这时相对m2来说是溢出了,最高位会变为1,即(a+b)最后会得到一个负的结果,这个结果是无意义的,所以计算时要注意结果是否溢出

测试代码:

image

设置带符号和10进制显示:

image

测试结果:

从图中可以看出,100 + 100 = 200 = 0xC8 =1100 1000 = –56

由于计算溢出,明显-56不是我们想要的结果

 

2> a + (–b)

当加上一个负数时,-b在计算机中的带符号的补码表示为: m2 + (m2 – b) = 2m2 – b = m – b   (m2 = 1000 0000,任何小于m2的数加上m2就相当于将其符号位变为1)

∴ a – b = a + (m – b)

         ≡ a – b (mod m)

即带符号的补码在负数运算中,仍然有效

但有个疑问,如果 a > b, a-b 为正数,计算完毕后符号位应该是0,当a < b时,a-b 为负数, 符号位应该是1,情况是这样的吗?

4.2.1 当 a > b时

∵ a < m2

∴ a – b < m2 – b < m2

∴ (a – b)不会超过m2,即不会溢出到符号位,符号位仍会为0

4.2.2 当  a < b时

a – b ≡ m + a – b  (mod m)

       ≡ 2m2 + a – b   (mod m)

       ≡ (m2 – b) + m2  + a   (mod m)

∵ m2 >  b

∴ (m2 – b) > 0

m > (m2 – b) + m2  + a > m2 

∴ a < b时,一定会向符号位溢出,即符号为一定会变为1

同时,a – b ≡ a – b (mod m2)

∴ 溢出后,低7位的数与a-b仍是同余的

所以,带符号位的补码运算,仍和不带符号位的补码运算规则一样,计算时将符号位带入计算,结果仍然一致,这样补码的第二个好处就出来了,即表示带符号的计算时,不用关心符号位。

 

4.2.3 当 a == b时,以及a和b中有0的情况

这个情况很简单了,就不证明了

 

5. 补码和反码

那么如何求一个数的补码呢,正数没问题,就是自身,但负数呢,上面已有简单的公式 –b → b = m – b, 但这里需要用到减法计算,而为了节省电路设计,我们用补码是为了省去减法器的,这就矛盾了,怎么办?

看过书的同学已经知道,用反码来计算 : 补码 = 反码 + 1

这个公式如何来的:

1> 反码的定义

我们知道,反码即是讲原来为0的位变为1, 为1的位变为0,

我们也知道一个集合加上其反,就等于全集

b + ~b = 1111 1111 = 28 – 1 = m - 1

∴ m = b + ~b + 1

b = m – b = (b + ~b + 1) – b = ~b + 1

这就是补码运算的著名公式的来源

posted on 2017-03-01 22:25  organic  阅读(3380)  评论(1编辑  收藏  举报