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)
这时,我们前面定义的补码还有用吗?运算的法则还需要改变吗?
1> a + b
当 a + b 小于 m2时,不会向符号位进位,所以结果就是 a + b,没问题
当2个正整数相加大于或等于m2时,就会向最高位进位,这时相对m2来说是溢出了,最高位会变为1,即(a+b)最后会得到一个负的结果,这个结果是无意义的,所以计算时要注意结果是否溢出
测试代码:
设置带符号和10进制显示:
测试结果:
从图中可以看出,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
这就是补码运算的著名公式的来源
原文:http://www.cnblogs.com/organic/p/6486676.html