计算机中的数字是如何表示的

由于计算机所使用的硬件特性,导致了在计算机的底层数字只能以二进制的形式进行表达。二进制和十进制一样,也是一种进位计数制,但是它的基数是 2。二进制表达式中 0 和 1 的位置不同,它所代表的数值也不同。例如,二进制数 0000 1010 表示十进制数 10。一个二进制数具有两个基本特点:两个不同的数字符号,即 0 和 1,逢二进一。

关于二进制的维基百科解释:https://zh.wikipedia.org/zh-cn/二进制

无符号数

无符号数顾名思义就是单纯的数字,没有正负号也没有小数点,是非负整数。这一类型的数字表示起来最简单,只需用二进制的方式写出即可。

为了更加清楚的解释无符号数的表示方法CSAPP中介绍了一种图形化的表示方法来帮助读者理解无符号数的编码方式。

图片来自b站up九曲阑干

二进制数从右往左的第i位即表示为2i12^{i-1},将不为0的项相加得到所表示的无符号数。

有符号数

在现实世界中,无符号数的局限性是相当大的,那么拥有正负号的数字该如何进行编码呢?

计算机中使用补码来表示有符号数,简单的来说就是在无符号数表示方法的基础上新增了一个符号位以表示正负。最高位若为0则表示正数,最高位若为1则表示负数。需要注意的是,符号位并不是单纯的表示数的正负性,它代表负权重的概念。

若一个有符号数的二进制形式如下:

图片来自b站up九曲阑干

那么它的十进制的计算方法为:

图片来自b站up九曲阑干

两个例子:

图片来自b站up九曲阑干

为了便于理解,补码也有对应的图形化表示方式:

图片来自b站up九曲阑干

其中蓝色代表正数,灰色代表负数,由图可知当最高位为1时得到的必定为负数。

数字的表示范围

无符号数的表示范围

对于无符号数,当字长为n时,其能表示的最大数为:1×20+1×21++1×2n1=2n11\times 2^{0} +1\times 2^{1} +\dots +1\times 2^{n-1} =2^{n} -1

字长为864的无符号数所能表示的范围图表:

图片来自b站up九曲阑干

有符号数的表示范围

有符号数与无符号数的区别在于其最高位充当符号位,所以只有n-1位能够表示数的大小。

所能表示的最大值为:1×20+1×21++1×2n20×2n1=2n111\times 2^{0} +1\times 2^{1} +\dots +1\times 2^{n-2} - 0\times 2^{n-1} =2^{n-1} -1

所能表示的最小值为:0×20+0×21++0×2n21×2n1=2n10\times 2^{0} +0\times 2^{1} +\dots +0\times 2^{n-2} - 1\times 2^{n-1} =-2^{n-1}

无符号数与有符号数之间的转换

对于一个二进制数:xw1xw2x1x0x_{w-1}x_{w-2}\dots x_{1} x_{0}

其对应的无符号数:U=xw1×2w1+xw2×2w2++x0×20U=x_{w-1}\times 2^{w-1} +x_{w-2}\times 2^{w-2} +\dots +x_{0}\times 2^{0}

其对应的有符号数:T=xw1×2w1+xw2×2w2++x0×20T=-x_{w-1}\times 2^{w-1} +x_{w-2}\times 2^{w-2} +\dots +x_{0}\times 2^{0}

可以得到:UT=xw1×2wU-T=x_{w-1} \times 2^{w}

做移项处理可得:U=T+xw1×2wU=T+x_{w-1} \times 2^{w}T=Uxw1×2wT=U-x_{w-1} \times 2^{w}

数字的位数扩展

有时候我们需要将一个数字的位数进行扩展,例如在C语言中将char类型转换为short类型。

零扩展

对于无符号数的扩展非常简单,假设要将unsigned char转换为unsigned short

图片来自b站up九曲阑干

根据无符号数的定义,只需要在新增加的高位补0即可。

符号位扩展

对于有符号数的扩展的情况稍微比无符号数的扩展麻烦一点点,还是用刚刚那个例子。

  1. 若有符号数的符号位为0:在新增加的高位补0
  2. 若有符号数的符号位为1:在新增加的高位补1

图片来自b站up九曲阑干

情况1比较容易理解,但是情况2是为什么呢?由数学归纳法可知:如果符号位为1的有符号数扩展1位成立,那么扩展k位也应当成立,下面证明扩展1位的正确性:

1xw2x1x0=1×2w1+xw2×2w2++x0×201x_{w-2}\dots x_{1} x_{0}=-1\times 2^{w-1} +x_{w-2}\times 2^{w-2} +\dots +x_{0}\times 2^{0}

11xw2x1x0=1×2w+1×2w1+xw2×2w2++x0×2011x_{w-2}\dots x_{1} x_{0}=-1\times 2^{w}+1\times 2^{w-1} +x_{w-2}\times 2^{w-2} +\dots +x_{0}\times 2^{0}

又因为:1×2w+1×2w1=1×2w1-1\times 2^{w}+1\times 2^{w-1}=-1\times 2^{w-1}

证明完毕

浮点数

现实中的数字当然不可能都是整数,计算机使用浮点数来近似表示实数。

设浮点数VV的表示方法为V=(1)S×M×2EV=(-1)^{S} \times M\times 2^{E},这里涉及三个变量:符号位S、阶码E、尾数M。

下面以C语言中的单精度浮点数float的表示方法为例:

图片来自b站up九曲阑干

一共占用32bit的存储空间,其中第31位表示符号位,第23-30位表示阶码,第0-22位表示尾数。

浮点数可以分为三类:

  1. 规格化的值(阶码字段不全为0且不全为1)
  2. 非规格化的值(阶码字段全为0)
  3. 特殊值(阶码字段全为1)

规格化的浮点数值

设阶码字段所表示的二进制数为ee,则阶码E=ebiasE=e-biasbiasbias为一个与阶码位数正相关的偏置量。设阶码位数为nn,则bias=2n11bias=2^{n-1}-1

设尾数字段所表示的二进制数为ff,则尾数M=1.f22f21f1f0=1+fM=1.f_{22}f_{21}\dots f_{1}f_{0}=1+f。这里的+1是从哪里来的?那是因为我们可以调整阶码所取的位数,保证1<f<21<f<2,这样1就没有必要显式的表示出来了。

计算出E和M,带入V=(1)S×M×2EV=(-1)^{S} \times M\times 2^{E}即可计算出规格化的浮点数的值。

非规格化的浮点数值

当阶码字段全为0且尾数也全为0时,浮点数所表示的值为0。由于符号位的存在,因此也分正0和负0,大多数情况下没有区别。

当阶码字段全为0且尾数不为0时,E=1biasE=1-biasM=fM=f,这与规格化的值解释不同。

计算出E和M,带入V=(1)S×M×2EV=(-1)^{S} \times M\times 2^{E}即可计算出非规格化的浮点数的值。

特殊的浮点数值

当阶码字段全为1,尾数字段全为0时,表示无穷大,符号位决定了是正无穷还是负无穷。

除此之外,还会遇到一些数字不为实数或者无法表示的情况,这种情况下统一表示为NaN(Not a Number),阶码字段全为1,尾数字段不为0。