2.2 进制换算
进制换算就是数值在不同的计数制之间进行的等值或等价换算,数值在换算前后保持不变,只是表达方式不同而已。计数制是为不同的计数单位制定的标准。
2.2.1 进位计数制概述
通常,在我们日常生活中最习惯应用的是十进制,但实际应用中也会使用其他计数制,例如十二进制(一打鸡蛋为十二个),六十进制(60秒为一分钟,60分钟为一小时)等。这种逢几进一的机制称为进位计数制。与C语言关系最密切的几种计数制是:二进制、八进制、十进制和十六进制。
二进制是只使用0和1的计数制,采用逢二进一的进位方式。由于可以使用0和1分别代表电路中的高电平和低电平,因此现代电子计算机数据存储机制大都采用二进制。由于二进制不易读写和计算,因此产生了八进制和十六进制计数制。八进制共有0、1、2、3、4、5、6、7八个数,采用逢八进一的进位方式。十六进制共有0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F十六个数,采用逢十六进一的进位方式。十六进制中字母部分不区分大小写,从A到F分别代表十进制数10、11、12、13、14、15。另外还有日常生活中最常用的十进制,共有0、1、2、3、4、5、6、7、8、9十个数,采用逢十进一的进位方式。如表2-1所示为几种进制的基本规则,其中基数、数码和位权的概念在下节讲解中做详细描述。
表2-1 几种进制的基本规则
2.2.2 二进制与其他进制转换
二进制数是由0和1组成的数字。那么,如何进行二进制和其他进制的转换呢?
1. 二进制转换十进制
二进制转换为十进制要分成两部分进行,一部分是整数部分,一部分是小数部分。转换方法如范例2.1所示。
范例2.1:将二进制数10010.11(2)转换为十进制数。
解析:通常将二进制数作这样的换算:
10010.11(2) = 1×24+0×23+0×22+1×21+0×20+1×2-1+1×2-2= 18.75(10)
算式中下标(2)和(10)称作基数,同时也是计数单位;2x称作位权;0和1称作数码。计算时幂指数可由右向左按数码位置从0算起。如右边最低位数码1对应的位权幂指数为0。由此我们计算得二进制数10010.11(2)相当于十进制数18.75(10)。
2. 二进制转换八进制
二进制到八进制的转换采用三位一体的计算方法,同样分为整数部分和小数部分两部分考虑。如下示例:
100111.01101(2) = 47.32(8)
二进制到八进制的转换方法为:将二进制数整数部分从右到左(小数部分从左到右),按每三位划分为一组,最左边(右边)不够三位的补0。如上述算式中100111.01101→100/111/011/010,将每组对应的八进制数代替二进制数,得到4/7/3/2,去掉分隔符,得到八进制数47.32(8)。二进制与八进制的对应关系如表2-2所示。
范例2.2:将二进制数1110100.101101(2)转换为八进制数。
解析:按照三位一体的方法,将二进制数从右到左每三位划分为一组,将各组改为对应的八进制数,得到八进制数的结果。计算流程如下:
1110100101101(2) → 001/110/100/101/101(2) → 1/6/4/5/5(8)=164.55(8)
3. 二进制转换十六进制
二进制到十六进制的转换采用四位一体的计算方法,计算时同样要将整数部分与小数部分分开。如下示例:
100111.01101(2) = 27.68(16)
和二进制到八进制的转换方法类似,二进制到十六进制的转换方法为:将二进制数整数部分从右到左(小数部分从左到右)按每四位划分为一组,最左(右)边不够四位的补0。如上述算式中100111.01101→0010/0111/0110/1000,将每组对应的十六进制数代替二进制数,得到2/7/6/8,去掉分隔符,得到十六进制数27.68(16)。二进制与十六进制的转换关系如表2-2所示。
表2-2 进制转换表
范例2.3:将二进制数1110100.101101(2)转换为十六进制数。
解析:按照四位一体的方法,将二进制数从右到左每四位划分为一组,将各组改为对应的十六进制数,得到十六进制数的结果。计算流程如下:
1110100.101101(2) → 0111/0100/1011/0100 (2) → 7/4/B/4(16)=74.B4(16)
2.2.3 八进制与其他进制转换
八进制数由0、1、2、3、4、5、6、7这8个数组成。与二进制到其他进制的转换类似,八进制向其他进制的转换也有一定的规则,本书只讨论整数形式的八进制向其他进制的转换。
1. 八进制转换为十进制
八进制向十进制的转换也要按照位权和数码作积再依次相加的方法。例如,下面的数制转换等式:
1270(8) = 696(10)
与二进制到十进制的转换类似,上式中八进制数1270到十进制数的转换计算公式为:
1270(8) = 1×83+2×82+7×81+0×80 =696(10)
算式中下标(8)和(10)称作基数,同时也是计数单位;8x称作位权;0、1、2和7称作数码。计算时幂指数可自右向左按数码位置从0算起。如右边最低位数码0对应的位权幂指数为0。由此我们计算得八进制数1270相当于十进制数696。
2. 八进制转换为二进制
八进制数向二进制数的转换可以看作是二进制到八进制的逆运算。八进制到二进制的转换方法为:将八进制数从右到左每位数字转换为3位二进制数,并去掉最左边的0位。
范例2.4:将八进制数5361(8)转换为二进制数。
解析:将八进制数从右到左依次转换为二进制数,计算方法为:
5361(8) → 101/011/110/001(2) → 101011110001(2)
注意:转换时每位八进制数字一定写满3位数的二进制数,如1 → 001,而不能写成1 → 01或者1 → 1。
3. 八进制转换为十六进制
八进制到十六进制的转换通常以二进制为中介,即先将八进制转换为二进制,然后再由二进制转换为十六进制。
范例2.5:将八进制数754231(8)转换为十六进制数。
解析:先将八进制数754231(8)转换为二进制数
754231(8) → 111/101/100/010/011/001(2) → 111101100010011001 (2)
再将二进制转换为十六进制数
111101100010011001 (2) → 0011/1101/1000/1001/1001 (2) → 3/D/8/9/9 (16) → 3D899 (16)
2.2.4 十六进制与其他进制转换
十六进制数是C语言中主要的赋值方式之一,同时也是二进制在C语言中的主要表现方式。在后续章节的C语言内存讲解及程序调试过程中,它将会得到广泛的使用。
1. 十六进制转换为十进制
十六进制向十进制的转换同样按照位权和数码作积再依次相加的方法。例如,下面的数制转换等式:
13FB(16) = 5115(10)
与二进制、八进制到十进制的转换类似,十六进制数13FB(16)到十进制数的转换计算公式为:
13FB(16) = 1×163+3×162+F×161+B×160 =1×163+3×162+15×161+11×160=5115 (10)
算式中下标(16)和(10)称作基数;16x称作位权;1、3、F和B称作数码。计算时幂指数可自右向左按数码位置从0算起。计算得十六进制数13FB相当于十进制数5115。
2. 十六进制转换为二进制
十六进制数向二进制数的转换可以看作是二进制到十六进制的逆运算,转换方法为:将十六进制数从右到左每位数字转换为4位二进制数,并去掉最左边的0位。
范例2.6:将十六进制数FB1A4(16)转换为二进制数。
解析:将十六进制数从右到左每位转换为4位二进制数。
FB1A4(16) → 1111/1011/0001/1010/0100 (2) → 11111011000110100100 (2)
3. 十六进制转换为八进制
十六进制转换为八进制同样需要二进制作中介。
范例2.7:将十六进制数3C6D(16)转换为八进制数。
解析:先将十六进制数3C6D(16)转换为二进制数
3C6D(16) → 0011/1100/0110/1101 (2) → 11110001101101 (2)
再将其转换为八进制数:
11110001101101 (2) → 011/110/001/101/101 (2) → 3/6/1/5/5(8) → 36155(8)
2.2.5 十进制与二进制的转换
十进制到二进制的转换分成两部分,一部分是整数部分的转换,一部分是小数部分的转换。
1. 整数部分转换
整数十进制到二进制的转换采用除以二取余再反向的方法。将整数作除以二取余运算,直到被除数为零,然后将余数反向顺序写出,就是整数部分的二进制表达。
范例2.8:将十进制数158转换为二进制数。
解析:首先对158进行除二取余运算
158/2=79……0
79/2=39……1
39/2=19……1
19/2=9……1
9/2=4……1
4/2=2……0
2/2=1……0
1/2=0……1
等号右边是每次运算所得的商,省略号后面是本次运算的余数。将所得余数自下而上按顺序从左到右写出:10011110,这就是十进制数158的二进制表示。
2. 小数部分转换
小数部分十进制到二进制的转换采用乘二取整再顺序写出的方法。将小数部分与2相乘,记录乘积的整数部分,将小数部分再与2相乘,记录乘积的整数部分,这样执行下去直到小数部分为0或满足要求精度。将所记录的整数部分按前后顺序从左往右写出,即得二进制形式。
范例2.9:将十进制数0.375转换为二进制数,要求精确到小数点后6位。
解析:对二进制数0.375作乘2取整运算
0.375*2=0.700……0
0.700*2=1.400……1
0.400*2=0.800……0
0.800*2=1.600……1
0.600*2=1.200……1
0.200*2=0.400……0
将上述所记录整数部分顺序写出:0.010110,这就是小数0.375的近似二进制表达。
作者心得:
十进制小数到二进制的转换经常遇到无限循环的情况,这时需要指定转换的精度,比如需要精确到小数点后8位,算到二进制小数点后8位即可停止运算。
2.2.6 机器数及其在内存中的存储格式
在计算机中,数据是以二进制形式存储的。而在内存中二进制数以字节为单位进行存储。通常,C语言中会经常提到描述二进制数的两个概念:bit和byte,前者是一个二进制位0或1,后者是指一个字节,表示8个二进制位。在内存中以二进制存储的数据称为机器数,机器数的存储有几种不同的表示方式,分别叫做原码、反码和补码。
1. 机器数
机器数的表示形式为:用0表示正数,1表示负数,其余位表示数值。通常,把在计算机内存中正、负号数字化的数称为机器数。C语言中的基本整型数据在计算机中通常用32位(即4个字节)来存储,后续章节中将会讲解有关32位机的概念。
2. 原码
原码是计算机中数据的存储方式之一。其表示形式为:数值用绝对值表示,在数值的最高位用0和1分别表示数值的正和负。
范例2.10:写出+35和-35的原码表示形式(32位表示)。
解析:首先确定数据的符号作为最高位,然后将数值转换为二进制数,以32位表示。
[+35] 原码=00000000000000000000000000100011
[-35] 原码=10000000000000000000000000100011
注意:0的原码有两种表示方式,即正0和负0,分别为:
[+0] 原码=00000000000000000000000000000000
[-0] 原码=10000000000000000000000000000000
3. 反码
反码在计算机中的表示方式为:正数的反码与原码相同,负数的反码是其原码数值部分按各位取反,符号位不变。
范例2.11:写出+35和-35的反码表示形式(32位表示)。
解析:首先分别写出两个数的原码,以32位表示
[+35]原码=00000000000000000000000000100011
[-35]原码=10000000000000000000000000100011
再将负数35的原码取反,得+35和-35的反码:
[+35]反码=00000000000000000000000000100011
[-35]反码=11111111111111111111111111011100
4. 补码
计算机补码的表示形式为:正数的补码与原码、反码相同,负数的补码是其反码加1,符号位不变。
范例2.12:写出+35和-35的补码表示形式(32位表示)。
解析:首先分别写出两个数的原码,以32位表示
[+35]原码=00000000000000000000000000100011
[-35]原码=10000000000000000000000000100011
再将负数35的反码加1,得+35和-35的补码:
[+35]补码=00000000000000000000000000100011
[-35]补码=11111111111111111111111111011101
5. 0的反码和补码
在反码和补码表示中,0是一个比较特殊的数字。由于0可表示为正0和负0,因此,0的原码和反码分别有两种表示形式,如下表示:
[+0]原码=00000000000000000000000000000000
[-0]原码=10000000000000000000000000000000
[+0]反码=00000000000000000000000000000000
[-0]反码=11111111111111111111111111111111
而对于+0和-0的补码,有:
[+0]补码=00000000000000000000000000000000
[-0]补码=00000000000000000000000000000000
可见0的补码表示是唯一的。