我们知道,对于有符号类型,计算机在处理的时候会把最高位当做符号位,0表示正数,1表示负数。举例来说(以下举例均针对8位有符号数):
0000 0001即为1,1000 0001即为-1,那么很容易得到8位有符号数值的表示范围应该是1111 1111 ~ 0111 1111,也就是-127~127,问题来了,-128是怎么来的呢?
在说明这个问题之前,先来补充点额外的知识:
在计算机的存储和计算中,统一的是采用补码进行处理和运算的,在弄清楚采用补码的好处之前,我们需要明确如下三个概念:
原码:是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。
反码:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
补码:正数的补码与其原码相同;负数的补码等于其反码+1。
很明显,文章开头的举例即是原码,它很符合我们的直观感觉,但是在计算的时候麻烦就来了,如果直接运算,例如1+(-1)=0,运算的时候0000 0001 + 1000 0001 = 1000 0010(-2),很显然是错误的,因此符号位和数值位必须分开处理!这会增加计算机的硬件开销。我们换成补码试试,1的原码0000 0001,反码0000 0001,补码0000 0001(正数三者一致);-1的原码1000 0001,反码1111 1110,补码1111 1111(1111 1110 + 0000 0001);因此1+(-1)用补码来运算就是0000 0001 + 1111 11111 = 0000 0000(最高位溢出舍弃),结果是正确的。
由此可见,可以将减法运算转化成加法运算,计算机只需要实现加法电路即可,并且原码到补码的转换和补码到原码的转换过程是完全一致的,为什么?可以简单证明如下:
假设32位机器,X为负数(最高位是符号位,对于负数,即为1),则有X(绝对值)+X(反)=0xFFFFFFFFF,因此X(绝对值)+X(反)+1=0xFFFFFFFFF+1=0;所以得到0-X(绝对值)=X(反)+1,根据之前的定义X(反)+1即为X的补码,于是有X(补)=X(反)+1=0-X(绝对值);
1)那么对于A、B>0,令K=-B,显然K是负数,那么有A-B=A+(0-K(绝对值))=A+K(补),减法运算就转化成了加法运算。
2)X(绝对值)=0-X(补);两边同时乘以-1 得到-X(绝对值)=-0-(-X(补))=0-(X(补(绝对值)))=X(补)(反)+1;-X(绝对值)即为原码X;因此上式说明了原码X等于X的补码取反后加一,也就是说X补码的补码就是原码。不论是原码求补码还是补码求原码方式完全一致。
回到最开始的问题,8位有符号数值的最小值为什么是-128而不是-127呢,我们列出来部分原码、反码和补码的对照表看看:
从上表中我们可以看出,0在原码和反码中有两种表示方式,而对应的补码只有一种表示方式,-128在原码和反码中无法表示,在补码中却可以表示(用-127的补码1000 0001减1得到1000 0000)。而计算机中数值以补码形式存储和运算,当然-128可以表示出来,因此8位有符号数值的范围是-128~127,同理其他位数(16、32、64)也可以以此类推。