一、原码、反码、补码和移码的一般求法
码制 | 一般求法 |
---|---|
原码 | 符号位用0表示正数,1表示负数,其余位不变。 |
反码 | 正数的反码与原码一样,负数的反码是对它的原码(除符号位外)各位取反。 |
补码 | 正数的补码与原码一样,负数的补码是其反码尾部加1。 |
移码 | 不管正负数,将其补码的符号位取反即可。 |
二、移码的“真正”求法
移码:移码表示法是在数X
上增加一个偏移量来定义的,通常用于表示浮点数的阶码。
如果机器字长为n,规定偏移 量为2n-1,则移码定义如下:
X 为纯整数:[X]移=2n-1+X(-2n-1≤X<2n-1)
X 为纯小数:[X]移=1+X(-1≤X<1)
【举个栗子】
[+1]移=28-1+1=129=1 000 0001
[-1]移=28-1 -1=127=0 111 1111
[+127]移=28-1+127=255=1 111 1111
[-127]移=28-1 -127=1=0 000 0001
[+45]移=28-1+45=173=1 010 1101
[-45]移=28-1 -45=83=0 101 0011
[+0.5]移=1+0.5=1.5=1 100 0000
[-0.5]移=1-0.5=0.5=0 100 0000
[+0]移=[-0]移=1 000 0000
三、原码、反码、补码和移码的“由来”
1. 原码
原码:符号位用0表示正数,1表示负数,其余位不变。
对于计算机,加减乘除是最基础的运算,要设计的尽量简单。
计算机辨别“符号位”显然会让计算机的基础电路设计变得十分复杂!于是科学家想出了将符号位也参与运算的方法。
我们知道,根据运算法则,减去一个正数等于加上一 个负数,即:1-1=1+(-1)=0,所以机器可以只有加法而没有减法,这样计算机运算的设计就更简单了。
在运算中,原码进行加法运算是没有问题的,但是在减法运算中会出问题,如:(D表示十进制)
1D-1D
=1D+(-1)D
=[0 000 0001]原+[1 000 0001]原
=[1 000 0010]原
=-2D
这个结果明显是错误的!
如果用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的。这也就是为何计算机内部不使用原码表示一个数的原因。
所以为了解决原码做减法的问题,出现了反码。
2. 反码
反码:正数的反码与原码一样,负数的反码是对它的原码(除符号位外)各位取反。
【举个栗子】
使用反码计算 1-1
过程如下:
1D-1D
= 1D+(-1)D
= [0 000 0001]原+[1 000 0001]原
=[0 000 0001]反+[1 111 1110]反
=[1 111 1111]反
=[1 000 0000]原
= - 0D
发现用反码计算减法,结果的真值部分是正确的。
而唯一的问题其实就出现在“0”
这个特殊的数值上。虽然人们理解上+0 和-0 是一样的, 但在这里 0 带符号是没有任何意义的,而且会有[0 0000000]原和[1 0000000]原两个编码表示 0。 于是补码出现了,解决了 0 的符号和 0 的两个编码问题,以及原码和补码进行减法运算时出现的问题。
3. 补码
补码:正数的补码与原码一样,负数的补码是其反码尾部加1。
使用补码计算 1-1 过程如下:
1D-1D
=1D+(-1)D
=[0 000 0001]原+[1 000 0001]原
=[0 000 0001]补+[1 111 1111]补
=[0 000 0000]反
=[0 000 0000]原
=0D
这样 0 用 0 000 0000 表示,而以前出现问题的-0 则不存在了,且在补码中 0 也只有一种唯一的表示形式。
假设机器字长为 8 位,使用补码则可以用[1 000 0000]补表示-128D;
(-1)D+(-127)D
=[1 000 0001]原+[1 111 1111]原
=[1 111 1111]补+[1 000 0001]补
=[1 000 0000]补
最终运算结果应为 [1 1000 0000]补,但由于机器字长为 8 位,最左边的“1”被丢掉了,故只剩下[1 000 0000]补。 -1-127
的结果应该是-128,在用补码运算的结果中,[1 000 0000]补就是-128。
但是注意因为实际上是使用以前的 -0 的补码来表示 -128,所以-128 并没有原码和反码表示(对-128 的补码表示[1 000 0000]补算出来的原码是[0 000 0000]原,这是不正确的)。
使用补码,不仅仅修复了 0 的符号以及 0 的两个编码的问题,而且还能够多表示一个最低数。这就是为什么 8 位二进制使用原码或反码表示的范围为[-127,+127],而使用补码表示的范围为[-128,127]的原因。
因为机器使用补码,所以对于编程中常用到的 32 位 int 类型,可以表示范围是(最高位是符号位):[-231,231-1],使用补码表示时可以多保存一个最小值 - 231。
假设机器字长为 n 位,则使用补码表示的最小的一个数是 -2n-1。
4. 移码
移码:不管正负数,将其补码的符号位取反即可。
常用来比较大小,一般会把浮点数的阶码用移码表示。当把数值用移码表示出来可一眼看出它们的大小,这样很容易判断阶码的大小,移码可用于简化浮点数的乘除法运算。
四、原码、反码、补码和移码的取值范围
码制 | 定点整数 | 定点小数 |
---|---|---|
原码 | -(2n-1-1) ~ +(2n-1-1) | -(1-2-(n-1)) ~ +(1-2-(n-1)) |
反码 | -(2n-1-1) ~ +(2n-1-1) | -(1-2-(n-1)) ~ +(1-2-(n-1)) |
补码 | -2n-1 ~ +(2n-1-1) | -1 ~ +(1-2-(n-1)) |
移码 | -2n-1 ~ +(2n-1-1) | -1 ~ +(1-2-(n-1)) |
【定点小数的取值范围的推算】
以补码为例:一个数用 n 位存储,用掉一个符号位后,还有 (n-1) 位,如果小数点在最右边,此时表示的是整数,可表示 -2n-1 ~ +(2n-1-1) 范围的数,把小数点左移 n-1 位,相当于除以 2(n-1) ,结果为:
-1 ~ +(1-2-(n-1))
同理可以将其他码制的范围求出来。
【总结】
总之,反码用来解决负数加法运算问题,将减法运算转换为加法运算,从而简化运算规则;
补码解决负数加法运算正负零问题,弥补了反码的不足。
反码与补码都是为了解决负数运算问题,跟正数没关系,因此,不管是正整数还是正小数,原码,反码,补码都全部相同。
将补码符号位取反即得到相应的移码。