2.1.1 进位计数制及其相互转换
在计算机系统内部,所有信息均采用二进制进行编码,主要原因如下。
-
二进制只有两个状态,只需使用具有两种稳定物理状态的器件即可表示每一位,硬件实现成本较低。例如,可用高电平和低电平分别表示1和0。
-
二进制的1和0恰好对应逻辑值“真”与“假”,为计算机实现逻辑运算和程序中的条件判断提供了直接支持。
-
二进制的运算规则极为简单,可通过基本的逻辑门电路高效实现各类算术与逻辑操作。
1. 进位计数制
常用的进位计数制包括十进制、二进制、八进制和十六进制。十进制是日常生活中最常用的计数制,而计算机内部主要使用二进制,并常借助八进制和十六进制来简化表示。
在进位计数制中,基数是指每个数位所能使用的不同数码的个数。例如,十进制的基数为10(数码为0~9),计数时遵循“逢十进一”的规则。以十进制数101为例,百位的1表示100,个位的1表示1,二者数值不同,是因为每一位的实际值等于该数码乘以其所在位置的位权。一个进位制数的数值,等于各位数码与其位权的乘积之和。
一个 进制数 的数值可表示为
式中, 是基数; 是第 位的位权; 是第 位的数码,取值范围为 。
-
二进制。基数为2,数码为0和1,计数“逢二进一”。第 位的位权为 。
-
八进制。基数为8,数码为0~7,计数“逢八进一”。由于 ,每3位二进制数恰好对应1位八进制数,两者转换十分便捷。
-
十六进制。基数为16,数码为0~9和 ( 分别代表10~15),计数“逢十六进一”。由于 ,每4位二进制数对应1位十六进制数,转换同样便捷。
为便于区分,常在数字后添加后缀字母来标识进制: 表示二进制数, 表示八进制数, 表示十进制数(通常省略), 表示十六进制数;此外,也常用前缀 0x 表示十六进制数。
2. 不同进制数之间的相互转换
(1) 二进制数转换为八进制数和十六进制数
对于一个既有整数部分又有小数部分的二进制数,转换时以小数点为界分别处理:整数部分 ,从小数点向左,每3位(八进制)或每4位(十六进制)分为一组,若最左侧不足3位或4位,则在高位补0;小数部分 ,从小数点向右,同样每3位或4位分为一组,若最右侧不足,则在低位补0。分组完成后,将每组直接替换为对应的八进制或十六进制数码即可。
反之,将八进制数或十六进制数转换为二进制数时,只需将每位数码分别替换为对应的3位或4位二进制数(必要时去掉整数最高位或小数最低位的0)。八进制数与十六进制数之间的转换,通常先转换为二进制数,再转为目标进制,这是最直接且不易出错的方式。
(2) 任意进制数转换为十进制数
采用按权展开相加法,将各位数码和对应的位权(基数的幂次)相乘,再求和。
例如,。
(3) 十进制数转换为任意进制数
通常采用基数乘除法,对整数部分和小数部分分别处理:
-
整数部分使用
除基取余法:不断除以目标进制的基数,记录余数,直至商为0;最先得到的余数为最低位,最后得到的为最高位。 -
小数部分使用
乘基取整法:不断乘以基数,记录整数部分,直至小数部分为0或达到所需精度。最先得到的整数为最高位,最后得到的为最低位。
最终将两部分的转换结果拼接,即得到目标进制数。
NOTE关于除基取余法和乘基取整法的原理,建议结合 进制数的数值定义公式理解,避免死记硬背。并非所有十进制小数都能用有限位二进制小数精确表示。一个十进制小数能被有限位二进制精确表示,当且仅当它可以表示成形如 的分数。例如,,而10不是2的幂(其质因数包含5),因此无法用有限位二进制精确表示。相反,任何有限位二进制小数都对应一个分母为2的幂的分数,因此总能精确地转换为十进制小数。这一特性在浮点数的表示与运算中尤为重要,需特别注意。
2.1.2 定点数的编码表示
1. 真值和机器数
在日常生活中,数通常用“+”或“-”号表示正负(正号常省略),如+15、-8。这类带有符号的数称为真值,即机器数所代表的实际数值。在计算机中,数的符号与数值部分一同编码:通常用“0”表示正,“1”表示负。这种将符号数字化的表示形式称为机器数。
例如,机器数0,101(逗号仅用于分隔符号位与数值位)表示真值+5。
2. 机器数的定点表示
根据小数点位置是否固定,计算机中的数值表示分为定点表示和浮点表示。定点表示用于表示定点小数和定点整数。
-
定点小数。表示纯小数,约定小数点位于符号位之后、数值部分最高位之前。若数据 (其中 为符号位, 为数值位, 为最高有效位) -
定点整数。表示纯整数,约定小数点位于数值部分最低位之后。若数据 (其中 为符号位, 为数值位, 为最低有效位)
事实上,在机器内部并没有小数点,只是人为约定了小数点的位置。因此,在定点数的编码和运算中,无须区分该数表示的是小数还是整数,而只需关心符号位和数值位即可。
定点数的编码表示法主要有四种:原码、补码、反码和移码。
3. 原码、补码、反码、移码
(1) 原码表示法
用机器数的最高位表示数的符号,其余各位表示数的绝对值。原码的定义如下。
例如,若字长为8位,,,则其原码表示分别为 ,。
对于 位原码整数,其表示范围为 (关于原点对称)。
NOTE零的原码表示有正零和负零两种形式,即 和 。
原码表示的优点: - 与真值的对应关系简单、直观,转换简便; - 用原码实现乘除运算比较简便。 缺点: - 零的表示不唯一,存在 两种编码; - 用原码实现加减运算比较复杂。
(2) 补码表示法
补码表示法的加法和减法运算均可通过加法器统一实现。正数的补码与原码相同,负数的补码等于反码的末位+1。补码的定义如下。
对于 位补码整数,其表示范围为 (比原码多表示一个负数,即 )。
- 几个特殊值的补码(位):
- (全0),零的补码表示是唯一的。
- (全1)。
- 最大正整数:(符号位为0,数值位全1)。
- 最小负整数:(符号位为1,数值位全0)。
- 模运算(了解)
在模运算中,一个数与它除以“模”后得到的余数是等价的。如 满足 (为整数),记为 ,即 、 各除以 后的余数相同。在补码运算中,,而 ,因此补码能够借助加法运算实现减法运算。
- 补码与真值之间的转换
真值转换为补码:对于正数,与原码的方式一样。对于负数,符号位取1,其余各位由其绝对值“按位取反,末位加1”得到。补码转换为真值:若符号位为0,则直接读作正数。若符号位为1,则真值为负数,其绝对值由补码数值部分“按位取反,末位加1”得到。
- 变形补码
为便于溢出检测,可采用双符号位的补码表示(又称变形补码),双符号位00表示正数,11表示负数。若总位数为 (高2位为符号位,其余为数值位),则变形补码定义为
在双符号位中,左符表示真正的符号位,右符用于判断“溢出”。
(3) 反码表示法(了解即可)
反码可视为从原码转换为补码的中间表示形式。
正数的反码与其原码相同。负数的反码由其原码的数值部分按位取反(末位不加1)得到。反码表示存在明显不足:
- 零的表示不唯一(存在 两种编码);
- 表示范围与相同字长的原码相同,比补码少一个最小负数 。
因此,反码在计算机中极少使用。
(4) 移码表示法
移码主要用于表示浮点数的阶码,且用于表示整数。其核心思想是将真值 加上一个固定偏置值,实现数轴整体右移。设字长为 位,偏置值通常取 ,则移码定义为
NOTE在IEEE754标准的浮点数中, 位阶码的偏置值为 ,如8位阶码的偏置值为127。
移码(设字长为 ,偏置值为 )的主要特点如下:
- 零的表示唯一,。
- 在相同字长下,移码与补码仅符号位相反(将补码的最高位取反即得移码)。
- 移码全0时,对应真值的最小值 ;移码全1时,对应真值的最大值 。
- 移码保持真值的大小顺序:移码值越大,对应真值越大,便于阶码比较。
四种编码表示的总结如下:
- 正数的原码、反码、补码相同;移码则不同。
- 原码与反码在数轴上关于原点对称,二者都存在+0与-0。
- 补码与移码的表示不对称,零的表示唯一,且比原码和反码多表示一个负数 。
- 原码可直观的比较大小(因数值部分即绝对值),而负数的补码和反码不能像原码那样直观判断。不过,在同为负数的前提下,补码或反码的数值部分越大,其真值也越大。
2.1.3 整数的表示
1. 无符号整数的表示
当所有二进制位均用于表示数值(无符号位)时,该编码称为无符号整数,简称无符号数。此时,数值隐含为非负整数。由于无须保留符号位,在相同字长下,无符号整数能表示的最大值大于有符号整数。无符号整数适用于仅涉及非负整数且结果不会产生负值的场景。例如,可用无符号整数进行地址运算,或用它来表示指针。
例如,8位无符号整数的最小值为00000000(0),最大值为11111111(),表示范围为 ;而8位有符号整数(补码表示)的最小值为10000000(),最大值为01111111(),表示范围为 。
2. 有符号整数的表示
有符号整数通过在数值位前增设一位符号位(0表示正,1表示负)来表示正负。虽然原码、反码和补码均可用于表示有符号整数,但现代计算机统一采用补码,因其具有以下优势:
- 零的表示唯一(无+0与-0之分)。
- 符号位可与数值位一同参与运算,使加减法统一为加法操作。
- 表示范围更大,比原码和反码多表示一个最小负数。
因此, 位有符号整数(补码)的表示范围为 。
2.1.4 C语言中的整数类型及类型转换
1. C语言中的整型数据类型
C语言提供了多种整型类型,其具体长度依赖于编译器和目标平台。常见情况如下:
- 短整型:short(或short int),通常为16位。
- 整型:int,通常为32位。
- 长整型:long(或long int),在32位系统中为32位,在64位系统中通常为64位。
在上述类型前添加 unsigned 关键字,可定义对应的无符号类型(如 unsigned int、unsigned short 等)。若未显式指定 signed 或 unsigned,则默认为有符号类型。
字符型(char,通常为8位)是一种特殊的整型,通常可按无符号整数解释。
在现代系统中,所有有符号整型均以补码形式存储。无符号整型则将全部位用于表示非负数值。因此,在相同位宽下,两者的取值范围不同。
2. 整型数据的类型转换
定点数在类型转换过程中,若涉及字长变化,则会触发两种基本操作:位截断与位扩展。
位截断:当长类型转换为短类型时,系统直接丢弃高位,仅保留低位部分。由于目标类型的表示范围较小,截断可能导致数值发生变化,具有较强的隐蔽性。位扩展:当短类型转换为长类型时,系统通过填充高位来保持数值语义不变。具体扩展的方式取决于源数据的符号性:
- 零扩展:用于无符号数,在高位补0。
- 符号扩展:用于补码表示的有符号数,高位重复填充符号位。
C语言支持通过强制类型转换实现不同类型间的转换,其语法为 TYPE b = (TYPE)a;,转换结果是一个 TYPE 类型的值。根据源类型与目标类型的字长和符号性,可分为三种情形。
(1) 长类型转换为短类型:位截断
转换规则:保留低位,丢弃高位。需要注意的是,此类转换不会触发任何异常或错误报告,具有很强的隐蔽性。
(2) 相同字长的转换:仅改变解释方式
转换规则:二进制位模式保持不变,仅重新解释其含义。所有二进制位均保持不变
相同字长的整型类型转换不改变位模式,仅改变对这些位的解释方式。
(3) 短类型转换为长类型:位扩展
转换规则:若源数据为有符号数,则执行符号扩展;若源数据为无符号数,则执行零扩展。