1.1 数和数制
发明计算机的最初目的是进行数值计算,计算机中首先表示的数据就是各种数字信息。随着应用的发展,现代计算机数据以不同的形式出现,如数字、文字、图像、声音和视频等。但是,在计算机内部,这些数据都是以数字的形式存储和处理的。
1.1.1 数字系统
我们通常使用数码符号(简称数码或数符)来表示数字(如0 ~ 9)。但是,在任何语言中的符号(字符)数量都是有限的,这意味着数码需要重复使用。
为了重复使用有限的数码符号,人类在长期的实践中摸索出数字的两类表示系统:位置化数字系统和非位置化数字系统。前者虽然使用相同的数码符号,但是其数值与位置有关而不一定相同。后者的每个数码符号有固定的数值,不随其位置变化。
巴比伦文明发展了首个位置化数字系统,称为巴比伦数字系统,它继承了闪族人和阿卡得人的数字系统,将其发展为位置化的六十进制(以60为底)数字系统。该底现今还用于时间和角度。例如,1小时为60分钟,1分钟为60秒。同样,1度为60分,1分为60秒。作为底为b的位置化系统需要b个符号(数码),我们希望一个位置化的六十进制系统有60种符号。但是巴比伦人没有符号0,而且通过堆叠1和10这两个符号构造出59个符号。
位置化数字系统中,在数字中符号所占据的位置决定了其表示的值。在该系统中,一个数字这样表示:
±(Sk-1…S2 S1 S0.S-1S-2…S-l)b
它的值是:
n=±Sk-1×bk-1+…+ S1×b1+S0×b0+S-1×b-1+S-2×b-2+…+S-l×b-l
其中,S是一套符号集;b是底(或基数),它等于S符号集中的符号总数,其中Si指该符号的位置是i。注意我们使用的表达式可以从右边或从左边扩展。也就是说,b的幂可以从一个方向由0到k-1,还可以从另一个方向由-1到-l。b的非负数幂与该数字的整数部分有关,而负数幂与该数字的小数部分有关。±符号表示该数字可正可负。
例如,二进制数(101.11)2在位置化数字系统中表示为101.11,它的值可以通过如下计算求出:
22 21 20 2-1 2-2 位置量
1 0 1 ? 1 1 数字
R = + 1×22 + 0×21 + 1×20 + 1×2-1 + 1×2-2 值
也就是,与二进制数(101.11)2相等的十进制数为4+0+1+0.5+0.25=5.75。
非位置化数字系统也使用有限的数字符号,每个符号有一个值。但是符号所占用的位置通常与其值无关——每个符号的值是固定的。为求出该数字的值,我们把所有符号表示的值相加。
罗马数字是非位置化数字系统的一个典型例子。该系统由罗马人发明,在欧洲一直使用到16世纪,如今它仍在体育比赛、钟表刻度和其他应用中使用。该数字系统有一套符号S={I,V,X,L,C,D,M},每个符号的取值如下所示。
符号 I V X L C D M
值 1 5 10 50 100 500 1 000
下面显示了一些罗马数字和它们的值:
III → 1+1+1 = 3
IV → 5-1 = 4
VIII → 5+1+1+1 = 8
XVIII → 10+5+1+1+1 = 18
XIX → 10+(10-1) = 19
LXXII → 50+10+10+1+1 = 72
CI → 100+1 = 101
MMVII → 1 000+1 000+5+1+1 = 2 007
MDC → 1 000+500+100 = 1 600
在计算机中使用的是位置化数字系统而不是非位置化数字系统。
1.1.2 计数与进制
数字是人们经常接触到的抽象代码,单独的数字不与任何具体事物相联系。大多数人使用的数字系统是以10为底的,也就是十进制。
进制的由来
十进制计数法的发明可能源于人类习惯使用10个手指计数。
玛雅文明发明的二十进制(以20为底)数字系统,称为玛雅数字系统。他们以20为底可能是因为他们使用手指和脚趾一起来计数。
十二进制曾经很流行。十二进制可能来自于一只手除拇指以外的四个手指的指
节个数,它们曾被用来计数。12很有用,它有很多因子。它是1到4最小的公倍数。十二进制的乘法和除法比十进制方便,而加法同样简单。
六十进制是苏美尔人和他们在美索不达米亚的继承者所使用的。60有大量因子,包括前6个自然数。六十进制系统被认为是十进制和十二进制合并过程中产生的。巴比伦文明的六十进制可能与天文历法计时有关。
十六进制曾经在中国的重量单位上使用过。
数学在某种意义上来说称得上是一种世界语言。不论哪种语言,所有的人都用同样的方式来表示数字,只是具体写法和发音不同。多数历史学家认为,数字最初创造出来是用来数数的,即计数。比如人数、财产数、商品交易量等。
我们现在使用的数字系统通常称为阿拉伯数字系统,或称为印度-阿拉伯数字系统。它起源于印度,但由阿拉伯数学家传入欧洲。印度-阿拉伯数字系统是与位置相关的,也就是说,一个数字依据位置的不同代表不同的数量。数字的位置和数字的大小一样,都同样重要。实质上,数字的位置更重要。100和1 000 000中都有一个1,但它们相差一万倍。
多位数中的各个位置(也就是我们所说的位)都有特定的意义,如图1-1所示。这7个方格可以表示从0~9 999 999的任何一个数字。
每一个位置(位)与10的一个整数次幂相对应。下面是10的各次幂:
与位置相关的计数系统的优点不在于它的便利程度,而在于当它用在不是十进制的系统中时也可以使用同样的方法,如二进制。
1.1.3 二进制和位
二进制数字系统是最简单的数字系统。其基数为2,数字的取值范围是0和l,计数规则是“逢2进位”。二进制数字系统中只有两个数字0和1,如果只需表达“是”或“不是”的话,不必用一个句子或者词语来表达,只要用一个位,即只要一个0或1即可。
位的英文是“bit” (比特)代表“binary digit”,其通常的意义是 “一小部分,程度很低或数量很少”。这个意义用来表示比特是非常精确的,1bit即一个二进制数字位确实是一个非常小的量。在计算机领域,位是组成信息块的基本单位。1位具备最少的信息量,更复杂的信息需要多个位来表示。
美国独立战争期间,Paul Revere使用灯光通知美国人敌人入侵消息的方法是一个利用位传递信息的例子。他告诉他的朋友:如果敌军今晚入侵,你就在北教堂的钟楼拱门上悬挂点亮的提灯作为信号。一盏提灯代表敌军由陆路入侵,两盏提灯代表敌军由海路入侵。没有入侵的情况就不挂提灯。如图1?2所示。
每一盏提灯都代表一个位。亮着的灯表示位值为1,未亮的灯表示位值为0。将上述问题抽象为数学表示如下:
a)0 0:敌军今晚不会入侵;
b)0 1 或1 0:敌军正由陆路入侵;
c)1 1:敌军正由海路入侵。
很容易想到,如果只有一盏提灯,那么只能表示有或没有敌军入侵,并不能表示敌人从哪个方向来。这个例子说明,一个二进制位只能表示两种状态,更多的状态可以通过多个二进制位的组合来实现。电子计算机利用的就是这个基本原理。
位是信息的基本单位,也是存储在计算机中的最小单位。在现代电子计算机中是用电子器件的两态性来实现二进制位。比如电子开关要么合上要么断开,如果用1表示合上状态,0表示断开状态,那么一个开关就能表示一个二进制位。当然,计算机需要定义足够大的数值范围才能工作。一个电子开关只有两个状态,即能表示一个二进制位;为了表示更多的数值状态,可以将多个开关并列使用。例如,8位宽度(即8个开关),这样就可以表示256 个不同状态(00000000 ~ 11111111)。通常,k 位的组合可以表达2k个不同状态,每个状态分别是k个0和1的位序列组合。我们称该0和1的序列为编码,每个编码对应一个特定的值或状态。电子计算机使用二进制来表示信息,仅仅是因为用电子器件实现二进制位比较简单,并不表示二进制比我们常用的十进制更先进。
莱布尼茨二进制与阴阳八卦
莱布尼茨不仅发明了二进制,而且赋予了它宗教的内涵。莱布尼茨一直与在中国传教的好朋友布维保持着频繁的书信往来。莱布尼茨曾将很多布维的文章翻译成德文发表刊行。恰恰是布维向莱布尼茨介绍了《周易》和八卦的系统,并说明了《周易》在中国文化中的权威地位。
八卦是由8个符号组构成的占卜系统,而这些符号分为连续的横线与间断的横线两种,即后来被称为“阴”、“阳”的符号,在莱布尼茨眼中,就是他的二进制的中国翻版。他感到这个来自古老中国文化的符号系统与他的二进制之间的关系实在太明显了,因此断言:二进制乃是具有世界普遍性的、最完美的逻辑语言。
1.1.4 八进制和十六进制
由于数据在计算机中最终以二进制的形式存在,所以使用二进制可以更直观地解决问题。但是,二进制数太长了,不适合人的书写和思考,用较大的进制数可以有效缩短数字串的长度,于是人们引入了八进制和十六进制。进制越大,数的表达长度也就越短。
八进制就是逢8进位,用0 ~ 7这8个符号组成数字表示,其基数为8。八进制数第0位的权值为8的0次方,第1位权值为8的1次方,第2位权值为8的2次方,……依此类推。
十六进制就是逢16进位,用0 ~ 9这10个数字,再加上A ~ F这6个字母共16个符号组成数字表示,其基数为16。十六进制数第0位的权值为16的0次方,第1位权值为16的1次方,第2位权值为16的2次方,……依此类推。
为了避免混淆,在使用不同进制时采用后缀表示进制,如用2、8、10、16表示二、八、十和十六进制数;也可用字母表示,通常用D表示十进制,用B表示二进制,用O或Q表示八进制,用H表示十六进制。例如,十六进制数FDA59B可以表示为(FDA59B)16或FDA59BH。
2、8、16分别是2的1次方、3次方和4次方。这三种进制之间可以非常直接地互相转换。1个八进制位可以表示3个二进制位,1个十六进制位表示4个二进制位。八进制数或十六进制数实际上是缩短了的二进制数,但保持了二进制数的表达特点,可以说是人们为了方便书写和记忆引入的二进制的缩写形式。
【例1-1】 用十六进制表示十进制数686。
解:
与十进制数686等值的十六进制数是(2AE)16
162 161 160 位置量
2 A E 数字
686 = + 2×162 + 10×161 + 14×160 值
1.1.5 不同进制间的相互转换
由于计算机使用二进制数,而我们日常生活中使用十进制,在进行程序设计和调试时,根据机器和编程环境的需要可能还分别会使用八进制和十六进制。下面我们讨论一下这几种数制之间的转换关系。
- 任意进制数转换为十进制数
根据以下公式我们将数码符号Si乘以其在数字系统中的位置量(或权值)bk并求和便得到在十进制中的数,其中b是基数。例如,在二进制中,b=2;在六十进制中,b=60。
n=±Sk-1×bk-1+…+ S1×b1+S0×b0+S-1×b-1+S-2×b-2+…+S-l×b-l
【例1-2】 将二进制数(100.11)2转换为十进制数。
解:
二进制数的基数为2,使用公式:
n=±Sk-1×2k-1+…+ S1×21+S0×20+S-1×2-1+…+S-l×2-l
二进制 1 0 0 ? 1 1
位置量 22 21 20 2-1 2-2
各部分结果 4 + 0 + 0 + 0.5 + 0.25
最后求和得到的结果是:(100.11)2=4.75
我们也可以使用竖式方法快速计算。例如,将一个十六进制数 2AF5换算成十进制数, 方法如下:
第0位: 5 × 160 = 5
第1位: F × 161 = 240
第2位: A × 162 = 2 560
第3位: 2 × 163 = 8 192+
----------------------------------------------
10 997
直接计算就是:
5×160 + F×161 + A×162 + 2×163 = 10 997
现在可以看出,所有进制换算成十进制,关键在于各自的权值不同。
- 十进制数转换为任意进制数
我们能够将十进制数转换到与其等值的任意进制数。我们需要两个过程,一是用于整数部分,另一个是用于小数部分。
将十进制数转换为任意进制数,整数部分的转换使用连除法,小数部分的转换使用连乘法。
(1)整数部分转换
我们称十进制数的整数部分为源,已转换好的整数部分的数为目标。反复除源并得到商和余数。余数插入目标中,商变为新的源。一直除到商为0为止,最后将所有余数逆序排列得到最终目标。
表1?1演示了如何将十进制数120转换成八进制数。要将120转换为八进制数,先将要转换的数120除以8,得到商15和余数0;把商15作为新的源继续除以8,得到商1和余数7;再把商1作为新的源继续除以8,得到商0和余数1;最后将所有余数逆序排列得到最终结果为170。
那么,如何将十进制数转换成十六进制数呢?十进制数转换成十六进制数是一个连续除16的过程。例如,将十进制数120转换成十六进制数,唯一变化是除数由8变成了16。如表1?2所示,120转换为十六进制,结果为78。请读者拿笔纸,按照类似的方法,将十进制数120转换成二进制数,看看结果如何。
(2)小数部分转换
小数部分的转化可使用连乘法。我们称十进制数的小数部分为源,已转换好的整数部分的数为目标。反复乘源并得到结果。结果的整数部分插入目标中,而小数部分成为新的源。最后将整数部分顺序排列得到最终目标值。
表1?3演示了如何将十进制数0.625转换为二进制数。因为0.625没有整数部分,该例子显示小数部分如何计算。这里是以2为基数。在左边一列写上这个十进制数。连续乘2,并记录结果的整数和小数部分。小数部分作为新源,整数部分作为目标。当小数部分为0,或达到足够的位数时结束。结果是0.625=(0.101)2。
表1?4演示了如何将十进制数178.6转换为十六进制数,要求精确到小数两位。鉴于178.6有整数部分和小数部分,我们需要分开转换。如表中所示,我们以16为基数,进行连除法和连乘法,并将分别求出的整数和小数(精确到小数两位)相加,得到的最终结果是178.6=(B2.99)16。
- 二进制和十六进制数互相转换
二进制和十六进制的互相转换比较重要。每个程序员都能轻松将数字从二进制转换到十六进制,反之亦然。这是因为在这两个进制数之间存在一种关系:二进制中的4位恰好是十六进制中的1位。
例如,对于二进制数1111,你可能还要这样计算:
1×20 + 1×21 + 1×22 + 1×23 = 1×1 + 1×2 + 1×4 + 1×8 = 15。
然而,由于1111才4位,所以我们必须直接记住它每一位的权值,并且是从高位往低位记:8、4、2、1。即,最高位的权值为23=8,然后依次是 22=4, 21=2, 20=1。
如果能记住以上权值,对于任意一个4位的二进制数,我们都可以很快算出它对应的十进制值。这样也就方便二进制和十六进制的互相转换。
【例1?3】 将二进制数111111011010010110011011转换为十六进制数。
解:
要将(111111011010010110011011)2转换为十六进制数,可以以4位一段,分别转换为十六进制,最终得到结果是:(FDA59B)16
1111 1101 1010 0101 1001 1011
F D ? A ? 5 ?? 9 B
【例1?4】 将十六进制数FD转换为二进制数。
先转换F:15。8 + 4 + 2 + 1=15,所以4位全为1,得到1111。
接着转换 D:13。8 + 2 + 1=13,得到1011。
所以FD转换为二进制数为1111 1011。
二进制数要转换为十六进制,就是以4位一段,分别转换为十六进制。反之亦然。
同样,如果一个二进制数很长,我们需要将它转换成十进制数时,除了前面学过的方法,我们还可以先将这个二进制转换成十六进制,然后再转换为十进制。
例如,将二进制数01101101转化为十进制数,可以将其先转为十六进制数6D,再转换为十进制数:6×16+13=109。
同理,我们还可以在二进制、八进制和十六进制之间快速转换。通过表1?5所示的进制转换表,我们可以总结出它们之间的规律。