要想知道计算机是如何进行运算的,就要先知道就计算机是如何进行存储数据的;
接下来,我们谈一谈计算机是怎样存储数据的;
数据存储:
我们知道生活中数据是以不同的形式出现的,如数字,文本等;以下将介绍数据的存储形式有数字,文本,音频,图像以及视频,我们将重点讨论数字的存储;
在计算机行业中,我们使用多媒体这一术语来定义数字,文本,图像,音频和视频的信息;
所有计算机外部的数据类型的数据都是采用统一的数据表示法转换后存入计算机的,我们称这种通用的格式为 位模式;
1:位(bit)是存储在计算机的最小单位,它通常有两种状态,0和1。位代表设备的某一种状态,这些设备只能处于这两种状态之一;
例如:开关要么是合上的,要么断开,那么用1表示合上的状态,0就是断开状态;
在今天,计算机使用各种各样的双态设备来存储数据的;
2:位模式是用来表示数据的不同种类型,它是一个序列,例如:1 0 0 0 1 0 1 0 1 1 1 1 1 1 0 1 展示了由16位组成的位模式,这就意味着要存储一个由16个位组成的位模式,就需要16个开关。通常长度为8的位模式成为一个字节;
不同类型的数据可以用同样的位模式存储在计算机当中,这需要一个中间的介质(程序);
例如:需要存储数字65 ,即 数字65→程序(数学公式)→01000001(存储器);
键盘输入字符‘A’→程序(文本编辑器)→01000001(存储器);
所以,同样的位模式可表示数字,字符或者图像等不同的场景,例如上面的两个例子,同样是01000001通过不同的程序表现出来的东西是不一样的,而计算机在进行存储东西的时候不需要辨别他们表示的是什么样的数据类型;
存储数字:
数字在存储到计算机内存之前,要通过进制转换(即转换成二进制),这里就不说进制转换的问题了,而在平常生活中数字有整数,小数,正负之分等问题,计算机使用两种不用的方式:定点和浮点法;
1:存储整数(整数是完整的数字,是没有小数部分的),例如 134 ,-145;
首先假设存储单元的最右边有一个小数点(实际上在计算机中是不存在),整数通常使用定点法存储在内存中;
但学过C语言的都知道整数类型分为两大类,第一类为无符号(0和正数的非负整数),第二类为有符号(即有正有负);
1.1无符号表示法(存储无符号整数)
由于不可能表示0到无穷大的所有整数,所以计算机通常会定义一个常量,这个常量被称为 最大无符号整数 ,它的值是2的n次方-1,这里的n是计算机分配用于表示无符号整数的二进制位数,例如:n的位数为4,它能表示的最大无符号整数就为15;
存储无符号整数有如下几个步骤:①将整数转化为二进制 ②如果二进制的位数不足n位,就在二进制的左边补0,使整数的位数与二进制的位数相等;如果大于转换后的整数的二进制位数大于n,那就会发生溢出的情况,我们后面将讨论溢出的情况,在这里不过度叙述;
例如:将数字7存储在8位的存储单元中,使用无符号表示法:
①将7转换为二进制1 1 1 ②在左边进行补0,凑成 0 0 0 0 0 1 1 1 ,共8位,所以数字7在计算内存中就为0 0 0 0 0 1 1 1;
如果是16位,那就补够0到16位,以此类推;
接下来,我们说一说溢出的问题:我们在写程序的时候如果数据发生溢出,就会导致结果不正确,这种结果导致的后果是小的,也可能是大的;
其实导致溢出的根本原因是 受存储单元的位数 的限制,所以表达整数的范围也是有限的,接下来,我们引出一个例子来说明这个问题;
这是一个时钟的表盘,它能所表示的最大整数为12,一旦超过12,就会从1从头开始,所以在这个表盘中表示3这个数,有无数种方式,即3,15,27......,(因为我们讨论的是无符号的存储,所以不考虑负数的情况,即-8...也可表示3);
所以在计算机中的n位存储单元中,就把其想象成为一个无限循环的表盘,假设有一个4位的存储单元,它能存储的无符号整数的范围为0到15,此时有个整数11,加上个9,此时,我们希望看到的结果为20,但是20超过了4位存储单元所能表示的最大整数,计算机得出的结果为4,下图解释了为什么是4的原因;
无符号整数表示法可以提高存储的效率,因为不用去考虑符号的问题。这就意味着所有分配到的存储单元都可以用来存储数字,只要用不到负整数就可以用无符号整数表示法;
1.2符号加绝对值表示法
其实在存储整数中这种方法并不常用,但该格式在存储部分实数的很常用,所以在这里讨论一下这种方法,请认真的看完会给你接下来的学习有帮助。
在此方法中,用于无符号整数的有效范围(0~2的n次方-1)被分为两个相等的子范围,前半段用于表示整数,后半段用于表示负数,假如,此时的n为4,那么表示的范围就是0000到1111,把它分为两部分:即正整数部分为:0000到0111,负整数部分为1000到1111;
在理解这种位模式需要有以下注意的地方:①负数在右边(在数学的数轴上表示为负数在左)②该表示法中有两个0,有个正0,一个负0;
在用符号加绝对值表示法存储一个整数的时候,需要用一个二进制位来表示符号(0表正,1表负),这就意味着在一个八位的存储单元中,用于存储数字的实际上只有7位,因此,该表示法的最大正数值为无符号最大数一半,所以在n位单元的可存储数字的范围为 ﹣(2的n-1次方-1)到 +(2的n-1次方-1);
例如:用符号加绝对值法将+28存储到8位的存储单元中
解:①现将该整数转换为二进制11100,因为要用最左边的一个二进制来表示符号,所以补0至7位,所以得到 0 0 1 1 1 0 0;
②再将左边的符号位补上,因为该数为正,所以左边补0;
如果是负的28呢?如果用符号加绝对值法将存储的 01001101、10100001复原成整数呢?请自行计算,答案为:10011100,+77,-33;
我们在上面提到过在存储无符号正数的时候会发生溢出(正数溢出),同样在存储有符号整数的时候同样会发生溢出,与上面不同的是它将发生两种溢出(正溢出和负溢出);
同上面一样我们依旧把其想象成为一个循环的表盘,不同的是在这个表盘中有正数和负数之分,而且有两个0;
假设:有一个4位的存储单元,用符号加绝对值表示法来表示溢出情况
我们这时保存一个整数5在存储单元中,又试图加上个6,这时就会发生溢出的情况,我们看下图:
以上计算,我们所期望的结果为11,计算机相应的-3;因为在一个循环的表示中,7是4位存储单元所能表示的最大正整数,而11超出了7,所以得到的结果为3,注意,-0也作为相加的一个数,千万不能忘记;同样的,负溢出也是如此;
通常情况下,符号绝对值法不用于存储整数,而用于部分实数,所以学习以上会对你学存储实数有很大的帮助,另外,符号加绝对值表示法通常用于采样模拟信号,例如,音频。
1.3二进制补码表示法(现如今几乎所有的计算机都使用二进制补码法来存储位于n位存储单元的有符号整数,所以它很重要)
在二进制补码法中,同符号加绝对值法相同,它将无符号整数的有效范围(0~2的n次方-1)分为两个相等的子范围,但与上面不同的是,第一个子范围用来表示非负整数,而第二个子范围用于表示负整数,而且只有一个0;
例如:如果n为4,该范围是0000到1111,分两半即0000到0111 和 1000到 1111,然后按照左负右正的原则进行排放,如下图所示:
在二进制补码表示法中,最左位决定符号,如果为0,则为正,1则为负;
在深入探讨这种表达法之前,我们先引入两个反码和补码这两种运算,反码就是将各个位进行反转,即0变1,1变0;
例如:求整数00110110的反码
第二个为整数的补码:正数(包括0)的补码是它本身,也就是不做补码运算,而负数的补码运算有两种,这里,我们介绍一种简单的补码运算。
该运算分为两步,第一步,从二进制数右边进行复制,一直到第一个1出现(将1复制下来),然后反转其余位,如下图所示:
这样就得到了该数的补码,计算机在以二进制补码存储整数的时候:遵循以下步骤:①将整数变成n位的二进制数,如果是0或正数,则按原样存储,负数,就要取其补码存储,
可能到这里,你可能会有疑虑,为什么要费时间做这种无聊的事,其实原因很简单:方便计算,因为该篇文章只说存储,所以你只要知道补码的概念和运算就行了,至于另一种补码的运算是:取其二进制数的反码,然后在做加一的运算,也可得到反码。
我们下面做一下二进制反码表示法的运算:
例:将28存储在8位存储单元中
解:因为28是正数,所以补码就是是其二进制原样,所以为 0 0 0 1 1 1 0 0(注意这里要进行补0)
例:将-28存储在8位存储单元中
解: 因为该整数为负,所以要取其二进制数的补码,原码为 0 0 0 1 1 1 0 0,进行补码运算后就为1 1 1 0 0 1 0 0
还原整数按照反方向计算即可;
在二进制补码法中最有趣的是符号位的0或1也参与运算, 而且该表示法仅有一个0,符号加绝对值法有两个0。
在最后就是溢出问题了,同其他表示法相同,二进制补码表示法同样会发生溢出,且有正负两种溢出
假设有4位存储单元,当我们保存整数5在存储单元中,在加上6,期望结果为11,结果为-5,如下图所示:
要注意的是该表示法只有一个0,同理,负数的溢出也是如此。
当今,二进制补码法是计算机存储整数的标准表示法,因为它能给运算带来很大的便利。
2:存储实数(实数即带有整数部分和小数部分的数字)例如23.7
尽管固定小数点法可以用于表示实数,到实际上可能会导致数据丢失,从而达不到所需要的进度。
例如:我们用小数点左边为14个码数,右边为2个码数,共16个码数的定点表示法,再存储1.00324,精度就会受损,实际存储为1.00。
因此带有很大的整数部分或很小的小数部分适不适合用定点法来存储的,应该用浮点表示法。
浮点表示法由三部分组成:符号,位移量,定点数,使用这种方法存储能够表示很大或很小的数,从而保证了实数的精度。
符号位:0为正,1为负;
位移量位:在下面的余码系统中会说明;
定点数:小数点位置固定的定点表示法;
2.1科学表示法
科学表示法在表述很大或很小的数时,形式更短更简洁,例如230000.00,用科学计数法就为2.3*10^5;
因为平常生活计算都是十进制,所以该方法引申到计算机存储中,即为01形式,且以2为底数,例如:101110.1,用科学计数法表示就为1.01101*2^4;负数同理;
2.2规范化
在这种规范化中,在小数点的左边都使用了唯一的非零数码,再十进制的系统中的数码可能是1到9,而二进制系统中的数码为1,如下图:
2.3符号、指数和尾数
在将一个二进制数规范化之后,我们会存储了一个数的三部分信息,也就是指数和尾数,下图是一个规范化后的例子:
符号是用来存储正负的;指数是定义为小数点移动的位数;尾数就是小数点右边的二进制数;
2.4余码系统
在存储尾数的时候实际上可以把它当做一个无符号的数看待,但是指数就不行了,因为指数有正负之分,在上面我们提到过用补码法来存储有符号的整数,但实际在存储实数的过程中,采用了一种叫余码系统的表示方法来代替补码法。
在余码系统中,正数和负数都可以作为无符号数存储,但为了表示正负的整数,一个正整数(称为一个偏移量)加到每个数字中,将他们移到非负的一边,这个偏移量的计算方法是2的m-1次方-1,m为内存单元的大小。
例如在四位存储单元中可表示16个整数,即-7到8,如果在该范围中加7个单位,就可以把它们统一的往右移将其变为正数,这种新系统称为余7或偏移量为7的偏移表示法;
这种新的表示法与移位前相比,优点在于所有的整数都是正数,在进行运算的时候不用考虑符号。
2.5存储浮点数
说到存储浮点数,就要提到IEEE所规定的的标准了,这里只讨论两种最常用的——单精度和双精度。
单精度格式采用总共32位来存储一个浮点表示法的实数。符号占1位,指数占8位,尾数占23位,此标准余127码,也就是2的m-1次方-1,因此,偏移量为127;
双精度格式采用总共64位来存储一个浮点表示法的实数。符号占1位,指数占11位,尾数占52位,此标准余1023码,也就是2的m-1次方-1,因此,偏移量为1023;
说了以上这么多,都是为了更好的学习计算机是如何存储实数的,下面将介绍存储实数的具体步骤:
①在S中存储符号(0或者是1);
②将数字转换为二进制;
③规范化;
④找到E和M的值
⑤连接S、E和M;
例:写出十进制数5.75的余127码(单精度)表示法
解:(1)符号为正,所以S=0;(2)十进制转二进制为 5.75=101.11;(3)规范化:101.11=1.0111*2^2;(4)E=127+2=129=10000001,M=0111,还需要在M的右边补19个0,让它变成23位;(5)链接S、E、M、,所以实数5.75存储在计算机中的实际数是0 10000001 01110000000000000000000。
负数也是如此,这里就不多说了,大家可以自己做一做练习;
最后在这里谈一谈如何得到单精度的最大值与最小值:我们可以在网上或者书籍能看到四字节的单精度float类型的范围约为:-3.4*10^38~3.4*10^38;
在这里先要说明两个小细节,因为8位存储单元索所能表示的最大正数为255,但IEEE754规定0和255用于表达特殊值,所以float指数的实际范围为-126到+127,具体的可以自己搜索IEEE754;
所以float类型的最大值就为:1.11111111...1(小数部分共23位)*2^127约等于3.4*10^38;最小值就为1.00000....1(小数部分共23位)*2^-126约等于-3.4*10^38;
存储文本:
在任何语言中,文本的片段是用来表示该语言中某个意思的一系列符号。例如英语中用A~Z表大写,a~z表小写,还有数字和符号等;
因此,我们就可以用位模式来表示任何一个符号。例如4个符号组成的文本 “CATS”能用4个n位模式表示,任何一个模式定义一个单独的符号;
那么问题来了,在某一种语言中,位模式要多大来表示一个符号?这就取决于该语言集到底有多少个不同的符号。假设,我们现在创建一种语言,只有大写英文字母一共26个,所以这种语言的位模式至少需要表示26个符号;
位模式的长度取决于符号的数量,他们的关系是呈对数的。例如,需要2个符号,位模式的长度就为:log2^2=1;需要四个就为:log2^4=2;
在2位的位模式下,共有00,01,10,11四种形式,这些形式的任何一种都能代表一个字符,例如00可以规定为:a,b或者是字符?,都可以;
这也就解释了为什么以%d形式输出字符,实际上输出的是数字,例如:字符0,以十进制输出为48;
下图表为符号数量和位模式长度的关系:
不同的位模式集合被设计用于表示文本符号,其中每一个集合我们称之为代码,表示符号的过程称为编码,下面介绍两个很常见的编码:
1 :ASCII
ANSI(美国国家标准协会)开发了一个被称为美国信息交换标准码的代码,该代码使用7位表示每个符号,该代码可表示2^7=128种不同的符号,具体请自行搜索;
2:Unicode
硬件和软件制造商一起联合设计了一种名为Unicode的代码,这种代码使用32位能表示的最大符号数量为2^32,这种代码不同部分被分配用于表示来自世界上不同语言的符号,现如今ASCII是Unicode的一部分。
存储音频:
音频表示音乐或者是声音,音频本质上与我们讨论的数字,文本都不同:因为音频是不可数的;
音频是随时间变化的实体,因此,我们只能测出每一时刻声音的密度,所以计算机在进行存储音频的时候也是按照密度进行存储的,例如每隔一秒或两秒;
音频是模拟数据的例子,我们就算能够得到一段时间的所有值,可不能把它们全部存储起来,这意味着需要无限个存储单元,显然这是不显示的,下图显示了一个音频信号:
存储音频第一步:
①采样:我们要从一段时间内选择数量有限的点来度量它们的值并记录下来,例如记录一秒钟内的10个样本:
接下来就是采样率的问题了,意思每秒钟要采集多少个样本才能还原出原始信号的副本,样本数量依赖于模拟信号中变化的最大值,换句话说,如果信号平坦的,就需要很少的样本,反之,就需要更多,一般来说每秒40 000个样本的采样率对于音频信号来说足够了;
②量化:每个样本测来的值都是真实的数字,这意味着我们要为每一秒的样本存储40 000个真实的值,因为样本的值可能是17.2等实数,因此在实际存储中会为每一个值当成无符号存储(因为存储数量是有符号的两倍),通过量化进行截取(量化:将样本的值截取为最接近整数值的一种过程),例如17.2,就可截取为17;
③编码:量化后的样本值需要被编码成位模式,有些系统会为样本附正值或者是负值,而有一些会使用无符号的整数来表示样本,但一般有符号的整数不会用补码法来表示,而是用符号加绝对值法,这在上面也提到过;
每样本位:对于每个样本系统需要决定分配多少位,现在一般都为16 、24、或者是32,每样本位的数量有时称为深度;
位率:把深度的数量称为B,把每秒样本数称为S,我们需要为每秒的音频存储S*B位,乘积称为位率R。例如:每秒40 000个样本,深度为16位,
位率R=40 000*16=640 000b/s=640KB/s。
当今主流的音频编码标准为MP3,它每秒采取44100个样本以及每样本16位,再用去掉那些人耳无法识别的信息进行压缩,这是一种有损压缩法,还有一种为无损。
存储图像:
因为不太重要,实在是不想写了,所以截了图。。。。
存储视频:
视频是图像(称为帧)在时间上的表示。一部电影就是一系列的帧一张接着一张地播放而运动形成的图像,将图像或帧转化为一系列的位模式并存储,组合起来就成为了视频,现在视频通常是被压缩存储的,常见的有MPEG。
以上内容绝大部分参考了计算机科学导论这本书,我学习后,按照自己的理解进行讲述,所以,有不对的地方,请指出。