1.3 计算机中的数值表示

1.3.1 机器数和真值

1.机器数

一个数在计算机中的二进制表示形式,称为该数的机器数。机器数是带符号数,数的最高位存放符号,正数为0,负数为1。

如计算机字长为8位,十进制数+3转换成二进制就是00000011;如果是十进制数-3,则转换成二进制为10000011。此时的00000011和10000011就是机器数。

2.真值

因为机器数的第一位是符号位,所以其形式值不等于真正的数值。如上面的带符号数10000011,其最高位1代表负,其真正数值是-3而不是形式值131(10000011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。

例如,00000001的真值=+0000001=+1,10000001的真值=-0000001=-1

1.3.2 原码、反码、补码

在计算机中,对一个数进行存储要使用一定的编码方式。机器存储一个具体数字的编码方式包括原码、反码、补码。

1.原码

原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。如8位二进制数为

[+1]=00000001 [-1]=10000001

因为第一位是符号位,所以8位二进制数的取值范围就是

[11111111, 01111111],即[-127,127]。

原码是我们最容易理解和计算的表示方式。

2.反码

反码的表示方法:正数的反码是其本身;负数的反码是在其原码的基础上,符号位不变,其余各位取反。举例如下。

[+1]=[00000001]=[00000001] [-1]=[10000001]=[11111110]如果一个反码表示的是负数,我们无法直观地看出它的数值,通常要将其转换成原码再计算其数值。

3. 补码

补码的表示方法:正数的补码是其本身;负数的补码是在其原码的基础上,符号位不变,其余各位取反,最低位加1(即在反码的基础上加1)。

[+1]=[00000001]=[00000001]=[00000001]

[-1]=[10000001]=[11111110]=[11111111]

对于一个补码表示的负数,我们也无法直观地看出其数值,通常也需要将其转换成原码再计算其数值。

1.3.3 原码、反码、补码的关系

计算机可以用3种编码方式表示一个数。对于正数,3种编码方式的结果都相同;对于负数,原码、反码和补码完全不同。

原码是可被人脑直接识别并用于计算的表示方法,为何还设计反码和补码?

在计算时,我们会根据符号位,选择对真值区域进行加减。但对于计算机,加法、减法、乘法已经是最基础的运算,需要尽量简单的设计,而让计算机辨别符号位会使计算机的基础电路设计变得十分复杂,于是有了让符号位也参与运算的方法。根据运算法则,减去一个正数等于加上一个负数,即1-1=1+(-1)=0,所以机器可以只有加法而没有减法,使计算机的运算设计更加简单。但如果用原码表示,让符号位参与计算,减法将会出错,所以计算机内部不使用原码表示一个数。为了解决原码做减法出错的问题,出现了反码。

例如计算十进制的表达式1-1=0。

1-1=1+(-1)=[00000001]+[10000001]=[00000001]+[11111110]=[11111111]=[10000000]=-0

用反码来计算减法,方案可行,但在“0”这个特殊的数值上,会出现+0和-0以及[00000000]和[10000000]两个编码。补码的出现,解决了“0”的符号以及两个编码的问题,具体如下。

1-1=1+(-1)=[00000001]+[10000001]=[00000001]+[11111111]=[00000000]=[00000000]

0用[00000000]表示,没有了-0的问题,而且可以用[10000000]表示-128。

(-1)+(-127)=[10000001]+[11111111]=[11111111]+[10000001]=[10000000]

-1-127的结果应该是-128,用补码方式运算后,[10000000]就是-128。但需注意,因为实际上是使用以前的-0的补码来表示-128,所以-128并没有原码和反码表示。(对-128的补码表示[1000 0000]算出来的原码是[00000000],这是不正确的)。

使用补码不仅解决了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数,这就是为什么8位二进制,使用原码或反码表示的范围为[-127, +127],而使用补码表示的范围为[-128, 127]。编程中常用的32位int类型,可以表示的范围是[-231, 231-1]。