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

1. 为什么偏向用十六进制?

    

    16进制数逢1612进制逢212^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 位系统为例,在内存中占两个字节,结构如下: (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位,溢出的那个 就不得不被丢掉啦! 于是就只剩下 (binary) 0000 0010 了。

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

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

    ""可以理解为存储空间的长度(从 到 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 呢?

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

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

    0 000 0011

    第一位 是符号位。结果为符号位 = 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

阅读更多
文章标签: 存储 hex 算法
个人分类: C/C++
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭