系统中所有的信息——包括磁盘文件、存储器中的程序存储器中存放的用户数据以及网上落上传送的数据,都是由一串位表示的。区分不同数据对象的唯一方法是我们读到这些数据对象时的上下文。比如,在不同的上下文中,一个同样的字节序列可能表示一个整数、浮点数、字符串或者机器指令。
计算机中的整数可以分为无符号整数和有符号整数两种类型。无符号整数不存在正负之分,在计算机中以其二进制真值的形式存放。而有符号整数由于有正负数的区分,表示相对复杂。
计算机中的符号数有三种表示方法,即原码、反码和补码。三种表示方法均有符号位和数值位两部分,他们的最高有效位就是符号位,都是用0表示“正”,用1表示“负”;而数值位,三种表示方法各不相同。
原码(Sign-Magnitude):
原码就是符号位加上真值的绝对值,即用第一位表示符号, 其余位表示值。比如如果是8位二进制:
[+1]原 = 0000 0001
[- 1]原 = 1000 0001
因为第一位是符号位,所以8位二进制数的取值范围就是:
[1111 1111 , 0111 1111]
即
[-127 , 127]
原码是人脑最容易理解和计算的表示方式。
反码(Ones\' Complement):
正数的反码是其本身;负数的反码是在其原码的基础上,符号位不变,其余各个位取反。
[+1] = [00000001]原 = [00000001]反
[ -1] = [10000001]原 = [11111110]反
可见如果一个反码表示的是负数,人脑无法直观的看出来它的数值,通常要将其转换成原码再计算。
补码(Two\'s complement)
正数的补码就是其原码本身;负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1 (即在反码的基础上+1)。
[+1] = [00000001]原 = [00000001]反 = [00000001]补
[ -1] = [10000001]原 = [11111110]反 = [11111111]补
对于负数, 补码表示方式也是人脑无法直观看出其数值的,通常也需要转换成原码在计算其数值。
为什么最终使用补码?
计算机的设计者为了简化运算器的结构、提高运算速度,将减法、乘法、除法都转化为了加法运算。根据运算法则减去一个正数等于加上一个负数,但是计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂,于是人们想出了将符号位也参与运算的方法。
原码和反码有一个奇怪的属性,那就是对于数字0有两种不同的编码方式。这两种表示方法把00..00都解释为+0;而值-0在原码中表示为10..00,在反码中表示为11..11。显然给0加上正负号都是没有意义的,而且影响了符号位参与运算的目标。补码解决了这个问题,0在补码中只有一种表示,那就是所有位都是0。
使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理。此外,补码与原码相互转换,其运算过程是相同的,不需要额外的硬件电路。