这节很有意思的点在于将整数的编码用数学的语言描述,这样从数学角度思考能更好的理解补码的作用,补码的一些好用的性质,也更加严谨。
C语言整型范围
C语言标准定义了每个类型至少需要表示的范围
实际机器上的范围只能更大不能更小。
无符号数的编码
设某整数二进制有w位,则称向量 = 为位向量。现在用来进行编码。
定义函数(B2U是Binary to Unsigned的缩写,长度为w),这个函数可将任意一个位向量映射到一个非负整数,如
的反函数是,将某个非负整数映射都对应的位向量。
至此,位向量和非负数可一一对应起来,这个唯一对应的位向量就是这个非负数的编码。
补码
用(Binary to Two's-complement的缩写,长度为w)定义补码。
与之前唯一的不同在于最高位的权重变为了,由此得到补码编码。
从定义式可以看出,最高位为1时表示的数一定是负数(因为除最高位外求和最大为),为0时一定是负数,这也是补码最高位被叫做符号位的原因。
例子 :
同理,定义为反函数。
接下来讨论32位有符号整数的范围。
根据B2T表达式可以看出最小值是只有最高位为1的情况,最大值是最高位为0,其余为1的情况。
由此得到取值范围
图中看出,这是由于非负数包含了0,而一半的位模式(最高位为1)表示负数,另一半表示非负数。
到这里可以明白,补码本身是可以跟原码,反码不产生任何关系的,只是作为一种独立的编码方式而已,而我们熟知的原码转补码的方法(取反后加一)这并不是补码的定义,只能作为一种计算方法。
这里贴出原码和反码的定义
这两种方式有一个奇怪的特性,对于0有+0和-0两种编码结果。
对原码和反码来说,+0都是[00...0],对于原码,-0表示为[100...000],对于反码,-0表示为[111...111]。
再介绍反码和补码名字的来源
对于非负数x,用作为-x的w位表示,由此得名补码。用[111...1]-x来表示-x,得名反码。
有符号数和无符号数之间的转换
C语言有时会发生类型转换,要记住,对于大多的C语言实现来说,转换不会改变位向量,也就是说存在内存中的01序列本身不会改变,只是改变了解释这个位向量的方式。
下面主要用数学化的形式来描述二者相互转换的规则。
有符号数转无符号数
定义,x为w位补码表示范围内的数。
x范围,内层函数结果为x的补码表示,将这个补码的位向量作为外层函数的输入,最终得到x类型转化后的无符号数,这个无符号数范围为
根据前面给出的B2U函数和T2B函数定义式
两式相减得
代入T2U定义式
故负数转无符号数会加上,正数不变。
无符号数转补码
定义,u范围,
范围
由有符号数转无符号数的推导过程
代入U2T定义式
也就是说u的最高位若为0,则补码相同,为1则需要减去
C语言中的有符号数和无符号数
C语言中默认数字是有符号的
cpp
int c = 1;//c是有符号数
加上后缀字符'U'或'u'即可创建无符号数。
如12345u
当有符号数赋值给无符号数或无符号数赋值给有符号数时就会发生隐式类型转换。
C语言中在执行一个运算时,若有无符号数,会隐式地将有符号数转为无符号数。
扩展数字位
无符号数拓展直接最高位补0,补码拓展补符号位。
截断数字
对于位向量,将其截断为k位时,高w-k被舍弃,得到新的位向量。
无符号数的截断
令,,则
补码截断
令,,则
将视作无符号数解析出,将作为无符号数截断后得到阶段后的无符号数,把新得到的无符号数转换成有符号数,最终得到截断结果。