跳至主要内容

理解计算机信息表达

你很可能听说过,计算机上使用的是二进制,也就是所有的东西都是0和1构成。那么为什么0和1可以表达各种信息呢?比如现在这篇文章,为什么这个也是0和1构成的,为什么看不出来0和1在哪里?

让我们先换一种方式理解二进制,0和1不是数学上的0和1,而是表示两个不一样的状态,比如灯是不是开着,一个人是男生还是女生。如果用1表示男生,0表示女生,那么10001就表示2个男生,3个女生。如果1表示灯开着,0表示灯关掉,用二进制表示3盏灯的状态就可能是110。也就是说虽然都是0和1,我们可以自己定义他们表达的意思。

要点1:相同数据可以表达不同的意义

那么如果我们需要表达的意思不单单是只有两种呢?比如我们需要表达一年的四个季度,那么需要4中状态,表达一个人的年纪,那就需要可以表示0到150(当然以后也许要200,300甚至更多),只有两个状态不够了,怎么解决?我们可以通过用多个0和1表达一个意思。比如针对四季,我们可以用00,01,10和11分别表示春夏秋冬。
一位的0和1(术语叫一个bit),表示两个状态,在这个两个状态基础上,再增加一位,那么因为新增加的这位也是两个状态,可以把原来能表示的状态增加一倍。比如四季的情况,原来有00,01,10和11四个状态,只需要2位, 现在增加一位,如果新增的位数值是0,那么0和之前的4个状态合起来有4个状态,分别是0(00),0(01), 0(10)和0(11),括弧里是原来的状态,如果新增的位数值是1,和原来的4个状态合起来,又可以表达4个新状态,分别是1(00),1(01),1(10)和1(11),总的状态是之前的2倍,也就是4x2=8种状态。再增加一位,变成4个bit,能表达的状态是3个bit的2倍,也就是8x2=16。以此类推,我们可以用16个bit表示2的16次方种状态,也就是65536种(6万5千多),或者我们可以用32个bit表示2的32次方种状态,也就是4294967296种(4百20多万),可以表达足够多的意思,比如所有的汉字,数学符号,英文符号等等。
回到表达年纪的问题,我们可以用7位二进制表达128种状态,8位表示256种状态,也就是8位就足够表达从0到150岁了。

要点2:只要位数够多,我们可以用0和1表达足够多的信息

现在我们知道我们可以用二进制表达任何意思,也知道可以通过增加位数提高可以表达的不同意义。那么如果不同的人有自己的表达方法,那么我们怎么可以分享信息呢?比如我用0表示女生,1表示男生,我可以很清楚的知道我的数据11001表示什么3个男生2个女生,一共5个,但是别人可能用0表示灯灭了,1表示灯亮着,那么同样是11001,在别人看来却表达3盏灯亮,2盏灯灭。怎样才可以大家看到的都一样呢?答案是大家统一规定一个信息的表示方法,比如大家都用7位表示一个字符,也就是最多有128种不同的字符,然后比如用65表示大写字母A,66表示大写字母B,48表示数字0,49表示1,依此类推。这就是ASCII字符集(Charactor Set)。只要大家都遵照这个规则,那么同样的数据就会表达同样的意思。
字符集有很多种,针对不同的语言,不同的需求,在不同的时期发展起来。比如简体中文就有GB 2312, GBK等,有西欧的ISO 8859-1 , 有阿拉伯语的ISO 8859-6等等。Unicode是一个可以涵括绝大多数已知语言和字符的字符集。

要点3:我们用字符集来满足文字信息交换需求

一个字节一个字节的使用效率不够高,为了有效的管理数据,正常情况下,我们把8位作为一个基本管理单位,叫做字节(byte)。在计算机环境里,处理2的整数次方是最有效的,通常来说,我们会希望处理1个字节,4个字节,8个字节等等,而不是希望处理3个字节,7个字节等等。这叫对齐。

字符集定义了所有在这个字符集能表示的字符和数字的对应关系。比如ASCII代码中用65代表大写字母A。同样的在Unicode中,65也是表示英文字符A(实际上,unicode最开始的127位和ASCII是一样的,可以认为Unicode是ASCII的超集,或者扩展)。这样看起来,大家都用一对一的方法,也就是字符集里面有多少字符,我们找到能够表示所有字符的二进制位数,然后按顺序一对一表示最简单。 对于比较小的字符集,比如ASCII,一共才128个,7位就足以,也就是一个字节(8个bit)就可以表示一个字符。但是对于比较大的字符集,比如unicode,在最新的12.0版本中,一共包含了超过13万7千(137,993)的字符。为了完全表示所有的字符,我们需要至少18位(17位可以表示2的17次方,也就是131072个数字,而18位可以表示2的18次方,也就是262144个不同数字)才能包含所有字符,由于基本单位是字节,那么我们需要3个字节(24个bit)才能表示所有的unicode字符。考虑到对齐,我们要4个字节表示一个字符。
在实际情况中,很多情况下,我们有大量文档是全英文的,或者大多数字符是英文的,本来英文1个字节就可以,那么如果用Unicode,我们需要4个字节,太浪费了。
为了解决这个问题,我们需要换个方法,不能用字符集里面的代码完全一样的数字来表示对应的字符。这就是编码,也就是encoding。比如UTF-8,就是这样一个对于Unicode的编码方式。它的特点是,利用ASCII只需要7位,也就是在一个字节中,还有一个bit是空的,那么如果第一位是0,那就直接和ASII一样,如果第一位是1,那么就和下一个字节,或下几个字节连在一起,表示需超过7位才能表达的字符。这样大多数英文字符就还是用一个字节表示,在英文字符为主的文章中,可以节省大量字节。

要点4:同样的字符集,可以有不同的编码(encoding)方法,使用得当,可以有效的节省空间

评论

此博客中的热门博文

格雷码(Gray code)

网上很多文章和视频提到 格雷码( Gray code ),和计算公式 (^ 表示异或, >> 是右移位):  num ^ (num >> 1)  但是没看到解释为什么,这里尝试用一种简单的方式理解一下(不是严谨的数学方法) 我们先来看看怎么产生格雷码 从一位的开始,这个简单: Decimal Binary Gray 0 0 0 1 1 1 利用1位的格雷码扩展到2位很简单, 先把现有的格雷码镜像一下,然后在上半部分前面加0,下半部分前面加1, 我们先来镜像: Decimal Binary Gray 0 00 0 1 01 1 2 10 1 3 11 0 然后加0和1 Decimal Binary Gray 0 00 00 1 01 01 2 10 11 3 11 10 从这个过程我们可以看到 上下两部分中,相邻的代码只有一位变化。原因很简单,我们用的是已有的格雷码,即便镜像了,相邻位变一位这个属性不会变 通过添加0和1,我们把原来格雷码表示的范围加倍了。而且不会产生重复 上半部分的最后一个编码和下半部分第一个编码,只有最高位不同 同时考虑这三点,这个格雷码的产生方法是正确的 扩展到3位也一样,先镜像现有的,然后结果的上半部分前缀0,下半部分前缀1: Decimal Binary Gray 0 000 000 1 001 001 2 010 011 3 011 010 4 100 110 5 101 111 6 110 101 7 111 100 我们可以用这个方法继续产生4位,5位,到任意位数的格雷码 现在我们来看看num ^ (num >>1 ) 是为什么 我们用3位的数字(0-7)来演示,原因是我们可以用之前产生的表来对照。 考虑数字2和6。6的二进制是0b110(我在二进制前面放0b,这样不会混淆),通过查表,6应该在表格的下半部分,2的二进制是0b010,在表的上半部分。然后你会发现,这个6和2的二进制表示的第一个bit和格雷码的第一个bit一样。其实当然是一样的,想一下,3位二进制数一半以0开始,一半以1开始,我们产生格雷码的时候,上一半是0,下一半是1。 ...