- 认识编程:以Python语言讲透编程的本质
- 郭屹
- 3452字
- 2025-02-28 23:31:02
2.3 进制转换及数据存储
人们生活中一直使用十进制系统,而计算机中一直使用二进制系统。这个系统是由科学家Leibniz发明的。
2.3.1 进制的转换
下面来看看进制之间如何转换。二进制到十进制是很简单的,一个二进制数根据公式其数值等于k*2n+k*2n-1+…+k*20,其中k只有0和1两个值,如11001,就等于1*24+1*23+0*22+0*21+1*20=25。
二进制转换成十进制的程序就不在这里演示了。
十进制转换成二进制有一点麻烦,用的一种除2余数法。
将十进制的25转换成二进制,按照如下步骤操作。
1)25除2,得到商数12,余数为1。
2)用12再除2,得到商数6,余数为0。
3)用6再除2,得到商数3,余数为0。
4)用3再除2,得到商数1,余数为1。
5)用3再除2,得到商数0,余数为1。
到此计算完毕,把余数反过来写,即11001就是最后的结果。
用程序来表达上述过程:

不用说读者也能看出,这个程序处理不了实数,因为实数有两部分,这个程序处理不了小数部分。小数部分不是通过除2取余,而是通过乘2取整得到的。
对十进制的0.125这个数,按照如下步骤操作转换成二进制。
1)把0.125乘2,得到0.25,取整数部分,是0。
2)把0.25乘2,得到0.5,取整数部分,是0。
3)把0.5乘2,得到1,取整数部分,就是1。
计算完毕,最后的结果就是0.001。
用程序实现如下:

测试0.125,得到结果0.001。
测试0.124,得到结果0.00011111101111100111011011001000101101000011100101011。
初次接触,读者可能会很惊奇,二进制表示0.124这么麻烦呐!可想而知有些小数会用到几百几千位才能够表示出来。许多人就会疑惑,那计算机里面究竟是如何表示的呢?
2.3.2 计算机如何存储数据?
计算机的存储空间有限,不能无止境地存储这些数,所以对小数会按照固定的精度存储。由此可以看到对小数的表示,在计算机中是不精确的,只是一个符合某种精度的大约值。
下面研究一下计算机内部数据的存储。计算机的最小数据单元是bit(比特),存一位,这个是不可再分割的。计算机中所有数据和信息都是靠很多bit表示和存储的。或许读者对bit没有什么概念,它能存多少信息呢?或者反过来说,一个数字、一个字符一首音乐需要多少bit表示和存储呢?
简单计算一下。
对于整数,从0到无穷,一个bit只能表示两个数字,0或1。如果有4个数字要表示,需要两个bit,即00、01、10、11,以此类推。8个bit可以表示256个数字,16个bit可以表示65536个数字,32个bit大约可以表示40亿个数字。
存储都是需要依靠物理设备的,落实到物理上,存储设备在不同的地方、不同的时代都不相同,只要能稳定地保持两种状态的器件都可以当成存储设备,最形象的就是开关。一个开关就是一个bit,存储65536个数字,需要16*65536个开关,这是一个比较大的数,如果存储40亿个数字,需要32*40亿个开关。
读者肯定很吃惊,这么多开关合在一起,得占多大的地方啊?想象得没有错,早期的计算机确实占地面积很大,有一座房子那么大,程序员是钻进计算机里面编写程序的。
早期的计算机还是“电老虎”,性能不高(相对于现在而言),经常坏(每15min烧掉一只真空电子管)。后来技术越来越进步,“开关”越做越小,也越来越快。
2.3.3 形象一点来看晶体管
1947年,美国贝尔实验室的巴丁、布拉顿、肖克莱三人发明了晶体管。晶体管尺寸小、开关速度快、发热量小,非常适用于计算机。晶体管有三极:e、c、b。高低电平变化,c和e导通或截断,通过这个表示0或1。

可以表示数(二进制),也可以表示逻辑运算(因为0和1可以认为是两种状态),这样实现了逻辑学的基础:布尔代数(布尔代数进行集合运算可以获取到不同集合之间的交集、并集或补集,进行逻辑运算可以对不同集合进行与、或、非。由英国数学家George Boole开创)。
比如1变为0,0变为1就是逻辑非运算NOT
比如下表是逻辑与运算AND

比如下表是逻辑或运算OR

比如下表是逻辑异或运算XOR


有了二极管和晶体管,通过电路比较容易实现上面的逻辑,这种电路叫门电路(因为通过开关表示,门可以开可以关,所以起了这个名字)。
与门如下图所示。

分析:当输入1为高电平,输入2也为高电平时,VT11导通,VT9导通,则输出点也为高电平,即为1。
或门如下图所示。

分析:当输入1或输入2为高电平时,VT6导通,输出点高电平,即数字量1。
非门如下图所示。

分析:当输入为高电平时,VT1导通,输出点电压为VT1的c、e之间的压降,即0.3V,即输出为数字0;当输入为低电平时,VT1的c、e之间未导通,输出电压为上拉的电压,+5V,即数字1。
现在用三极管做出了逻辑运算,可以看出数据和运算在二进制中是一回事,所以最后在底层统一到门电路了。
门电路的符号如下图所示。

先看如何组合一下这些基本门电路让它存储状态,也就是逻辑上存一位bit。作为存储器,基本的要求是保持状态稳定,远古的时候用小石子和绳结,它们不会轻易变化,这个固有的物理特性就让它们适合存储,而手指则不适合,一去干别的事情,手指头的状态一下子就变化了。
使用如下双稳态电路作为存储器。

上面的电路组合了两个或非门,一个的输出是另一个的输入,这样构成双稳态结构。当S端设为1时Q总是1,当R端设为1时Q总是0。
当电路上一秒还在“S=0,R=1”状态时(此时Q=0,非Q=1),突然变成了“S=0,R=0”,此时可以发现,由于Q=0,S=0,非Q仍然是1。非Q=1,R=0,Q仍然是0,双稳态电路就做到了保持Q的状态。电路有了记忆。所以可以存储一位bit。这个电路叫作基本RS触发器。
把上面的RS触发器组合在一起,就可以存储多位了,如图所示。

现在终于有了能存储数字的器件了。从远古人类用小石子、绳结作为存储器,经过了数千年,人们发明了速度快得多、体积小得多的存储器。
要注意,现在讲的是计算机内部的寄存器,人们平时还会接触硬盘一类的存储设备,它是如何存储的呢?它们的原理不同,用的是磁介质,这里不做进一步解释了。
2.3.4 抽象一点来看数据存储逻辑
手上有了这些器件,知道它们能从物理上表示和存储bit,后面不需要再管实际的物理部件了,只要从逻辑上去理解如何进行数据表示和存储即可。
先看正整数。计算机内部一般用定点法存储正整数。所谓定点,就是不管这个数是3还是30000,都用同样多的bit表示,如用16位bit表示整数。3表示为0000000000000011(前面补充14个0)。这样的好处是整数在计算机内部都是对齐的。定点法表示整数会有一个最大值,如16位最大能表示的数字为65535。
正整数是无符号的,那么如何表示有符号的数呢?表示负数就必须想办法把这个符号表示出来。前面曾提到过,在计算机中一般用补码表示。规则简单地说就是取反加1。这个规则的另一种表述是:从最右边的位开始,逐个复制,遇到第一个1,复制这个1之后的操作变成逐个取反。笔者更加喜欢第二种表述,因为这是一种机械操作式的表述,让人好像看到了计算机内部是如何变化的。
用补码表示数,最左边高位的符号如果为1,表示为负数,0则为正数。
如-28,28的二进制为00011100,求补码为11100100,-28就是用补码11100100表示的。
28还是表示为00011100。
因为最高位是符号,所以补码表示的数字范围是无符号表示的一半,比如用8位表示无符号数,表示范围为0~255,而用补码表示范围为-128~127。
下面再看看如何存储实数。
前面介绍过,实数在二进制表示中有精度问题,IEEE规定了几种格式,用得比较多的有两种:单精度和双精度。
这种结构是一种科学计数法,用S(符号)、E(指数)和M(尾数)来表示,底数定为2,即把一个浮点数表示为尾数乘以2的指数次方再添加符号。

一个实数,可按照如下办法存储。
1)先确定符号位,0为正数,1为负数。
2)数字转成二进制。
3)规范化科学计数。
4)计算E和M值。
5)组合SEM。
举例,5.75的存储方法如下。
5.75是正数,所以S符号位为0,转成二进制为101.11。把101.11规范化成1.0111*22。这样识别出指数为2,按照IEEE规定加127偏移量,得出E为129,二进制表示为10000001。
尾数为0111(由于规范化之后小数点前永远为1,所以就不存储了),后面加19个0补齐23位,M为01110000000000000000000。拼在一起的存储格式为01000000101110000000000000000000。
除了数,还会接触到很多别的类型,如字符、音频、图像,那这些是如何存储的呢?其实一切都要数字化。
2.3.5 字符的编号
对于字符,要规定字符的编码,比如a,规定编码为61。标准化组织制定了一些标准来统一编码,用于共享和交换,比如常用的ASCII,如下图所示。

音频是模拟信号,要全部记录下来需要无穷的存储空间,所以通过采样进行数字化,比如在一秒钟的音频中采集100个点,记录点上的值(16位记录一个点的值),为了不失真,要求采样足够密,按照MP3的标准,每秒要采样44100次。
可以计算一下,按照MP3规范,一秒的音频的位率是44100*16=640KB/s。
这下可以理解为什么音频文件会比文本文件大很多了。
图像也是类似的道理,可以逐点记录图像的值。JPEG和GIF都是这方面的标准。同样可以想象到,存储图像也需要很大的空间。
讨论到这里可以看出,计算机内部把一切数字化,存储上存的就是数字。而运算也简化成了基本运算,即加法、移位和逻辑运算。