在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;
同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。
原码存在的问题
在计算的时候,人脑可以知道第一位是符号位,我们会根据符号位,选择对真值区域的加减
而计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂,于是人们想出了将符号位也参与运算的方法.
我们知道, 根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0,
所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法.
我们来看原码: 计算十进制的表达式: 1-1=0
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
这个结果不正确.这就是为何计算机内部不使用原码的原因
模
模的概念可以帮助理解补码。
“模”是指一个计量系统的计数范围。如时钟等。
计算机也可以看成一个计量机器,它也有一个计量范围,即都存在一个“模”。例如:
时钟的计量范围是0~11,模=12。表示n位的计算机计量范围是0~2^(n)-1,模=2^(n)。
“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。
任何有模的计量器,均可化减法为加法运算。
例如:假设当前时针指向10点,而准确时间是6点,
调整时间可有以下两种拨法:一种是倒拨4小时,即:10-4=6;另一种是顺拨8小时:10+8=12+6=6
在以12为模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替。
对“模”而言,8和4互为补数。实际上以12模的系统中,11和1,10和2,9和3,7和5,6和6都有这个特性。共同的特点是两者相加等于模。
对于计算机,其概念和方法完全一样。n位计算机,设n=8,所能表示的最大数是11111111,
若再加1成为100000000(9位),但因只有8位,最高位1自然丢失。又回了00000000,所以8位二进制系统的模为2^8。
在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以了。
把补数用到计算机对数的处理上,就是补码。
使用补数
实现化减法为加法
如: 7 - 4 = 7 + 4的补数
但是取补数怎么取呢?根据上面的原数+补数=模这一特点,用模-原数的方式取补数还是有减法啊?有没有别的办法取补数?
取反操作对计算机而言是一个简单的操作,只需切换每一位的状态,
原数+取反后的原数=模-1,即:取反后的原数+1=模-原数=补数
7 - 4 = 0111 - 0100 = 0111 + (0100的补数) = 0111 + (0100取反+1) = 0111 + (1011 + 1) =
0111 + 1100 = 10011(溢出舍去最左边的1) = 0011 即 3
即可无须特殊处理符号位(符号位如上参与加法运算),达到简化硬件设计,提高效率,降低成本的目的
补码体现了统一处理的优势,降低复杂度的同时降低成本
软件工程也应如此,该统一的地方一定要统一
特性
- 一个负整数(或原码)与其补数(或补码)相加,和为模。
- 对一个整数的补码再求补码,等于该整数自身。
- 补码的正零与负零表示方法相同。
16位补码示例
16位二进制数能够表示2的16次方即65536个数.
有符号16位二进制补码所能表示的数值范围是-32768到32767.
无符号16位二进制补码所能表示的数值范围是0到65535.
补码是循环的,最大的数32767,加1就等于最小的数-32768,
0111 1111 1111 1111 ( 32767) 加1以后等于
1000 0000 0000 0000 (-32768)
无符号的(32768)1000 0000 0000 0000取反再加1所得到的-32768的补码
仍然是1000 0000 0000 0000
所以1000 0000 0000 0000在无符号时是32768,有符号时是-32768
并且有符号时,对1000 0000 0000 0000这个数一直加, 可以得到如下的循环:
1000 0000 0000 0001(-32767)
1000 0000 0000 0010(-32766)
1000 0000 0000 0011(-32765)
……
1111 1111 1111 1100(-4)
1111 1111 1111 1101(-3)
1111 1111 1111 1110(-2)
1111 1111 1111 1111(-1)
0000 0000 0000 0000( 0) (溢出的一位丢失了)
0000 0000 0000 0001( 1)
0000 0000 0000 0010( 2)
0000 0000 0000 0011( 3)
……
0111 1111 1111 1100( 32764)
0111 1111 1111 1101( 32765)
0111 1111 1111 1110( 32766)
0111 1111 1111 1111( 32767)
1000 0000 0000 0000(-32768)