您的位置:首页 > 其它

原码、反码、补码之来龙去脉

2011-10-18 17:36 225 查看
1. 为什么偏向用十六进制?



16进制数逢16进1,2进制逢2进1。2^4=16。

16进制进一位,恰好对应二进制进四位(十六进制10 = 二进制1 0000, 十六进制100 = 二进制1 0000 0000)。

从而,16进制数(如: 2A9),可以按位依次转成二进制: 2(0010)A(1010)9(1001)。每一位对应二进制四位。

即: 2A9 = 0010 1010 1001

故,当看到十六进制数 fffffff3 时,就能立即方便地反应出其二进制的数值了。

后面会在数值前标明进制说明:

二进制: (binary) 0000 0011

八进制: (octal) 325

十进制: (decimal) 289

十六进制: (hex) ffff fff3

2. 数据在内存中存储的方式



int i = 3;

以 16 位系统为例,i 在内存中占两个字节,结构如下: (binary) 0000 0000 0000 0011

为了方便,我们设系统为8位的,int 只占一个字节。即 int i = 3 的存储结构为: (binary) 0000 0011

3. 溢出与"模"的概念



从上可以看出,int 占一个字节的话,能储存的数的范围是 (binary) 0000 0000 -> (binary) 1111 1111,或者说 (decimal) 0 -> (decimal) 255

如果两个数想加,超过了这个范围,那么就会"溢出"。

例如,在这个存储中, (decimal) 4 + (decimal) 254 = ?

从二进制的角度看:

(binary) 0000 0100 = (decimal) 4

(binary) 1111 1110 = (decimal) 254

------------------------------------- +

(binary) 1 0000 0010 = (decimal) 258

因为只能存储8位,溢出的那个 1 就不得不被丢掉啦! 于是就只剩下 (binary) 0000 0010 了。

也就是说:(decimal) 4 + (decimal) 254 = (decimal) 2 (丢弃溢出丢出来的奇怪结果!这可是后面能进行负数运算的根源所在呢!)

丢掉的那个溢出数,即 (binary) 1 0000 0000 = (decimal) 256,就被称为 "模"。

"模"可以理解为存储空间的长度(从 0 到 255,长度正好是 256)。当两个数的和超过了存储空间的长度时,我们就取其对模的余数(如:258 mod 256 = 2)。

4. 为什么要用补码?

按前面假设,int 只占一个字节,即8位存储结构。很明显,这里只能存储指定范围的正数,且只能进行加减运算(结果也只能属于这个范围,溢出的就丢掉)。

负数怎么办呢?

显然,计算机是不认识负数的。负数也可以理解为减运算。

根据前面的丢弃溢出的运算,我们有:

(decimal) 4 + (decimal) 254 = (decimal) 2

而显然的,我们还有:

(decimal) 4 - (decimal) 2 = (decimal) 2

也就是说,在丢弃溢出的运算条件下:

+ (decimal) 254 = - (decimal) 2

哈哈,是不是说 + 254 = - 2 啊?!

不错,在这种丢弃溢出的算法中,是可以这样的。(可以理解为钟表往前拨8小时(+8),与向后拨4小时(-4),是一样的结果)



也就是说,在存储范围内,如果 a + b = "模",那么,在丢弃溢出的运算条件下: + a = - b;- a = + b

那么,我们就可以直接用 + 254 表示 - 2 啦!(因为它们在运算中的效果是一样的嘛!)

从而 (binary) 1111 1110,既可以表示 + 254, 也可以表示 -2 喽!

可是,怎么让计算机知道,我要的值是 - 2,而不是 + 254 呢?

在前面再加个标志嘛。比如,加个 0 表示我们要取的是 + 254,加个 1 表示我们要取的是 -2。

可是我们的存储就只有8位唉! 最后只好将存储中第一位的值拿出来作为符号位(0表示是正数,1表示是负数),剩下的7位用来存储数值,如下:

0 000 0011

第一位 0 是符号位。结果为: 符号位 = 0,值 = (binary) 000 0011

嗯,原先8位存储结构,能存放数值为 0 => 255,现在为了支持负数,第一位用来做符号位,于是能表示的数值范围变成了 - 128 => + 127(取 1 000 0000 = - 128)。

显然,现在数值的"模"也变成了 128。与前面相同运算,与 - 2 对等的正数也变为 + 126 了。

于是,根据 - 2 = + 126,将 - 2 写成二进制:



(1). 符号保留在符号位,即,符号位 = 1 (0表示是正数,1表示是负数)

(2). 数值则直接用 + 126,即数值为 111 1110

结果:- 2 = 1 111 1110

而正数的二进制表示则简单、直观得多: + 126 = 0 111 1110



这样,只需将8位存储结构拿出第一位来表示正/负符号,我们就可以存储正/负数,并进行加减运算了(负数用对应的正数表示,反正运算结果是一样的;而减运算就是加负数嘛!)。



原码:

对任一个数,将其转换为二进制。然后在前面按"0表示是正数,1表示是负数"加一个符号位,就是原码啦!如:

(decimal) 2 的原码 = (binary) 0 000 0010

(decimal) -2 的原码 = (binary) 1 000 0010

反码:

对正数,等于其原码。

(decimal) 2 的反码 = (binary) 0 000 0010

对负数,顾名思义,就是对原码的数值的部分取反。如:

(decimal) -2 的反码 = (binary) 1 111 1101

补码:

对正数,等于其原码。

(decimal) 2 的反码 = (binary) 0 000 0001

对负数,等于反码再加1。

(decimal) -2 的反码 = (binary) 1 111 1110

明白了吧? 这不正是前面我们用 + 126 表示出来的 - 2 的值嘛!

是的,根据前面的分析,与负数相对等的那个正数,它们绝对值的和正好等于"模"(可以理解为钟表往前拨8小时(+8),与向后拨4小时(-4),是一样的效果)。对一个二进制数来讲,先取反,再加1,再加上原数,正好是一个和为"模"的过程,反映到十进制上就是通过-2找+126的过程。

所以说,负数在计算机中是用补码表示的。

对于零而言,(binary) 0 000 0000 的补码为 (binary) 1 000 0000,所以就取 (binary) 1 000 0000 为 -128。所以,负数的表示范围为: - 128 => + 127。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: