程序员的“怪癖”
现在人们常规的计数方法是十进制数,但是我们要知道,人类历史上绝不是只有十进制计数方法。有人认为,英文单词中的十二是twelve而不是ten-two之类的表示方式,就说明了当时人们没有使用十进制数计数。人类学研究表明,人类曾经使用过的计数方式还有七进制、十二进制和二十进制等。
按照十进制数的计数规则,数的每一位都是0~9之间的数字,加减运算的进位借位规则是:满10进1,借1当10。10是十进制数制的基数。
很明显二进制数的计数规则是:数位上的数字是0~1之间的数字;加减运算的规则是:满2进1,借1当2;2是二进制数的基数。以此类推,我们可以得到七进制、八进制等数制规则。但是十六进制数的规则有其特殊性,那就是9以上的单个数字如何表示?这里不得不求助于字母啦。对于十六进制数来说,每个数位上的数字必须是0~9的数字或者a~f之间的字母,其中a代表10,b代表11,以此类推。书写时字母的大小写是无所谓的。二进制数和十六进制数的加减法示例如图1-11所示。
图1-11 二进制数和十六进制数加减法示例
任何数制的数都可以写成数字序列,为了能相互区分,我们可以将数字序列括起来加上基数的下标。十进制数可以省略下标。比如512是十进制数,(1001)2是二进制数,(715)8是八进制数,(57af)16是十六进制数。
我们最习惯使用十进制数,但是程序员需要理解计算机世界中的数据,而计算机世界中的数据都是以二进制方式存放的,所以程序员必须能够将十进制数转换为二进制数进行思考,然而二进制数书写起来的确太费劲,一个很小的数却可能需要非常多的数位。所以,在编程环境中人们被禁止输入二进制数,而是用八进制数和十六进制数来代替。相应地,程序员必须学会将十进制数转换为八进制数或者十六进制数。直接将十进制数转换为八进制数或者十六进制数特别容易出错,一个巧妙的做法是先将十进制数转换为二进制数,再将二进制数转换为八进制数或者十六进制数。
数制转化涉及数字的解析式表示。我们可以将5237解析为:5237=5×103+2×102+3×101+7×100。将这个例子做一般的推广,设某个n位十进制的整数是An−1An−2…A1A0,它的解析式则是:
An−1An−2…A1A0=An−1×10n−1+An−2×10n−2+…+A1×101+A0×100
如果A0是7,A1是3,A2是2,A3是5,而其他Ai是0,这个数就是5237。
同样一个数,它可以表达为任何数制的解析式。设十进制数An−1An−2…A1A0,它的二进制数的最后形式是Bm−1Bm−2…B1B0,那么它的解析式是:
An−1An−2…A1A0=(Bm−1Bm−2…B1B0)2=Bm−1×2m−1+Bm−2×2m−2+…+B1×21+B0×20
从中可以看出,我们将十进制数An−1=An−2…A1A0不断除以2并取其余数,直到商为0,就可以得到从低到高的二进制数的数字序列。比如20的二进制数是(10100)2,求解过程见表1-1。
表1-1 20的二进制数求解过程
要想将一个二进制数转换为八进制数,只需要将一个二进制数从低向高每3位划为一组(位数不足时补0),再将每组3位二进制数转换为1位八进制数即可;要想将一个二进制数转换为十六进制数,只需要将一个二进制数从低向高每4位划为一组(位数不足时补0),再将每组4位二进制数转换为1位十六进制数即可。每组数的转换是非常简单的。
记住,每4位二进制数从高向低的权值分别是8、4、2、1,即23、22、21、20,熟悉了这个8421,转换起来非常迅速。比如(10101)2→(010 101)2→(25)8,(111011)2→(0011 1011)2→(3b)16
十六进制数是思考计算机世界中数据的最快捷方便的方式。一旦熟悉了十六进制数的表示方式,程序员不需要将一个十六进制数转换为十进制数也能思考。在计算机世界中,8位二进制数被定义为一个字节(Byte),1024(即210)个字节被定义为1KB,1024KB被定义为1MB,1024MB被定义为1GB,1024GB被定义为1TB。这些都是用来表示存储容量的基本单位。
整数的数制转换规则很简单,就是不断除以基数取余数的结果。那么十进制小数可以这样转换吗?当然不行!我们将任何实数划分为整数部分和小数部分。整数部分可以按照上面的办法进行转换,而小数部分则需要新的规则:不断乘以基数取整数部分,直到积为零为止。你可以通过分析小数部分的解析式来找到这个规则。
然而你试试将0.3转换为二进制数看看!你会发现0.3的二进制数是一个无限循环小数。在计算机中,任何数据的存放都是有限数位的,不可能存放无限循环小数。因此,整数可以精确存放在计算机中,实数并不总能精确存放,但存放的小数位数越多就越精确。也就是说,实数在计算机中的存放是有精度的。
精度不仅在二进制中存在,任何进制都存在精度问题。任何有限位数的十进制数都无法精确表示1/3,这是常识。尽管如此,我们常人还是喜欢十进制数。十进制数给人的感觉是连续的,而二进制数或者十六进制数给人的感觉是离散的。那么,这个世界是连续的还是离散的呢?很明显,世界是连续的还是离散的与我们采用的数制没有关系,任何测量出来的数据,在一定的精度上都是离散的,在另一个精度上又可以看作是连续的。
小贴示:这个问题实际上是一个哲学问题,在学习C语言时我们也可以联想到哲学问题,这很有趣。哲学在当今时代的大学生们看来是最没有用的东西,然而我们每个人的生活都离不开哲学,有些人的哲学是基于自己生活经验的哲学,是没有经过严肃理性思考的哲学。而大学生应该使自己的哲学理论化一点。哲学与宗教不同。信仰宗教可能使你看破红尘,但是学习哲学不是这样。哲学是一种引导你思考问题的学问。只有拥有了丰富的哲学知识,才能对这个红尘世界看透不看破,才能获得积极而平和的心态。个人认为,认真阅读几本马克思主义哲学的原著是大学生的必修课,不是为了考试,而是为了哲学地思考这个世界和自己。