一些数学符号的解释:
求和符号: ∑5i=0xi=x0+x1+x2+x3+x4+x5
x⃗ 表示向量x ,一组1和0,例如[1010 1100]。
xi 此位向量中第i个位。 向量中最右边的位为第0位,向左依次加1.
mod 取模运算 ,求余数运算。 1 mod 2=1 // 3 mod 2=1。在二进制系统中,一个位向量 mod 2k 相当于将此向量截断到k位 。 例如 [1111 1111] (十进制为255)mod 23 =[0000 0111](十进制为7)。
111100002=24010 右下角下标表示此数的进制表示格式,例如2为二进制。
B2U。 Binary to Unsigned的简写,既是将二进制位模式通过无符号编码进行解释,转换。此外还有B2T,U2T 之类。
⌞x⌟ x向下舍入。 ⌞6.12⌟=6 。
⌜x⌝ x向上舍入。 ⌜6.12⌝=7 。
二进制既是将不同长度的“位”(一个1或0)组合在一起再加上不同的解释以赋予含义。同样的机器代码[ 1000 1010] 在不同的编码模式下的意义是不同的。
这里分析一下无符号编码,既是C语言中的unsigned int, 与补码编码,C语言中的 int。通过五个方面进行学习:1,将位模式转换为两种编码格式下的值的过程的函数缩写,2,位向量在两种编码格式下的取值范围,3,位向量在两种编码格式下的扩展方法,4,理解溢出这个现象,并理解如何通过取余运算正确算出溢出后的值,5,了解通过移位替代乘除法这项优化是如何实现的。
最后了解一个变量在两种格式之下的转换发生了什么,以及计算机如何将1和0转换为字符。
1,无符号编码
–函数缩写
如果有位向量
x⃗ 既[xw−1,xw−2,...,x1,x0]
,则在无符号编码中它的十进制值为:
例如:把[01011110]转换为十进制数值
1,加下标:
0716051413121100
2,下标转化为2的幂并乘以相应的位,然后相加:
0∗27+1∗26+0∗25+1∗24+1∗23+1∗22+1∗21+0∗20=94
–取值范围
假设有一个8位的位向量,最小值无疑是0,它的最大值是 [1111 1111],通过上述公式我们知道它在无符号编码中的十进制值为2的9次方加2的8次方依次到2的0次方,这个2的n的递减次幂的和等于2的n+1次方减1,既2的10次方减1。
所以位向量
[xw−1,xw−2,...,x1,x0]
的取值范围为:
–扩展
一个通过无符号编码来解释的位向量如果想在不影响它所代表的值的情况下扩展它的位,它的长度,就得在它的最高位之前添加0,这种方法称为零扩展。
例如:将一个较小的无符号数据类型 unsigned short ( 假设为[1111 1100 0011 1111])转换到一个较大的类型 unsigned
在最底层此变量抓换为位向量后将会变为 [0000 0000 0000 0000 1111 1100 0011 1111]
–溢出
已知 w位的位向量的最大值为 2w−1 。如果编程过程中,赋予它的值超过了此最大值,实际值将会减掉 2w 。例如,某个变量a的位向量为 [1111 1111] , a+=1 后 理论上它应变为 [1 0000 0000]。 但由于a 只有8位,它无法保存最高位的1,因此实际情况是在底层位向量将会变为 [0000 0000] 。既是赋值给他 [1 0000 0000]( 29 )后减掉了 29 ,变为了0。
加法溢出这种现象是有规律的,可以用取模运算计算变量溢出后的值。
对于一个w位的无符号整数变量a,如果用它储存w位的无符号整数变量b和c的和,那么运算后它的值为:
假如b+c= [1111 1111] 小于 2w [1 0000 0000], (b+c)mod(aw) 总是等于他自身,所以公式2试用于所有非溢出情况。另一方面,可计算变量溢出后的实际值,例如变量b [1111 1111] 加c [0000 0101] 等于 [1 0000 0100] 溢出后 为 [0000 0100] (254+5)mod(2w) 会得4, 等于[0000 0100],既溢出后a实际的值。
用取模运算计算减法溢出后的值:
设一个w位的无符号整数变量a,如果用它储存w位的无符号整数变量b和c的差,则:
例如b=[0000 0000] , c=[0000 0001], 得出的结果将为 2w−1
用取模运算计算乘法溢出后的值:
设一个w位的无符号整数变量a,如果用它储存w位的无符号整数变量b和c的积,则:
例如:b=5 [101],c=3[011]在无符号编码下它的值为[101]*[011]=[1 111], 溢出后为[111]
15mod23=7 [111],公式计算结果与实际值一致。
–用移位替代乘法和除法
无符号整数的乘法,除法相对于加减法,移位,位级运算来讲是很慢的。乘法可以被移位操作和加法的组合替代掉。
1,左移代替乘法。
已知一个位向量 x⃗ ,[xw−1,xw−2,...,x0] 。如果在它的最低位添加k个0,则它会变为, [xw−1,xw−2,...,x0,0,...,0]
计算这个位向量的十进制值的公式是上面的公式1的一种变形,由于
xk至x0
都是0,求和公式并没有太大的改动,只是公式1中的每个位都要再乘以
2k
,既变为了 :
结论: x⃗ ∗2k 等于在位向量的低位添加k个0 ,也既是整个位向量向左移k位。
例:
x⃗ ∗10=x⃗ ∗(23+2)=x⃗ <<3+x⃗ <<1
x⃗ ∗5=x⃗ ∗(22+1)=x⃗ <<2+x⃗
2,右移代替除以2的幂
设位向量
x⃗ ,[xw−1,xw−2,...,x0]
设 k 为
0≤k<w
设
x,
为一个w-k位的位向量
[xw−1,xw−2,...,xk]
设
x″
为
[xk−1,...,x0]
根据公式1对
x,x,,x″
进行求和:
将公式6-2乘以 2k 得出:
而公式6-4( 2kx, )加上公式6-3等于公式6-1,也既是x,所以:
再观察公式6-3,位向量最小值为 [00…0], 最大值为[11…1],此最大值的求和公式为 ∑k−1i=02i=2k−1 。
所以 x″ 的取值范围是 0≤x″≤2k−1 或 0≤x″<2k 。
由于 x″ 总是整数并且总是小于 x″ ,那么 x″/2k 一定是一个小于1的小数,向下舍入总是得0:
公式6-5两边除以 2k 并向下舍入,得 ⌞x/2k⌟=⌞x,+x″/2k⌟ ,因为 x, 为整数, x″/2k 又一定为一个小数,所以等式右边舍入后既是x’。
再考虑下如果对位向量x进行右移k位的操作, [xw−1,xw−2,...,x0] 右移k位会得到 [0,...,0,xw−1,xw−2,...,xk] ,位移后的向量实际上就是上面的 x, 。
结论:对于位向量x, [xw−1,xw−2,...,x0] 右移k位等同于 x/2k 。
无符号整数的除法也可以用向右移位来替代,但只局限于除以2的幂的情况。
[0000 1100]=12
右移两位
[0000 0011]=3
12÷22=3
2,补码编码
–函数缩写
补码编码的最高有效位
(xw−1)
为负权,它表示的是
xw−1∗(−2w−1)
。当它为1时,它将会减去后面的正数之和,通过这种方式表示负数。当它为0时,
0∗(−2w−1)
为0,位向量的值为正数。
公式: 如果有位向量
x⃗ 既[xw−1,xw−2,...,x1,x0]
,则它在补码编码中的值为:
–取值范围
w位补码的最小值为[100…0]既是
−2w−1
,最大值为[011…1] 既是
∑w−2i=02i
。
这种编码模式又造成了一种现象。考虑一下
23−(22+21+20)=1
我们发现
xw
总是比
∑w−1i=0xi
大1,因此 w位补码 的最小值的绝对值 总是比 它的最大值大1。正负数两端并不是对称的。
–扩展
与无符号编码的扩展方式不同。由于最高有效位为负权,在不改变位向量的值的情况下对它进行扩展的方法叫做符号扩展,考虑一个位向量[1111 1111] ,它的值为 27−26−25−...20=1 。符号扩展一位后为[1 1111 1111] ,它的值依然为1,根据此数学规律,在位向量为负数的时候,既是最高位为1的时候,在它的最高位左边添加1不会影响它的值。而在位向量为正数时,在最高位左侧添加0,同等于上面无符号编码中的零扩展,也不会改变它的值。
这种在补码编码中对变量进行扩展的方式称为符号扩展—–在最高位左侧添加最高有效位的副本。
–溢出
补码编码的溢出除了考虑赋予变量的值超出它能表示的最大值的情况外,还要考虑赋予它的值超出了它能表示的最小值的情况。
设有w位的有符号整数变量a
[aw−1,aw−2...a1,a0]
a的取值范围为
−2w−1≤a≤2w−1−1
补码加法溢出与无符号编码相同现,可以用取模运算计算变量溢出后的值。
对于一个w位的变量a,它储存w位的变量b和c的和,那么运算后它的值为:
但 补码编码的溢出除了考虑赋予位向量的值超出它能表示的最大值的情况外,还要考虑赋予它的值超出了它能表示的最小值的情况。
例如:b=-1[1111 1111] c=-128[1000 0000]
b+c=[1 0111 1111]=-129
溢出后为 [0111 1111]=127
根据公式11的条件3,公式10计算结果为:(b+c)+2^w=-129+256=127
减法溢出:
对于一个w位的变量a,它储存w位的变量b和c的差,那么运算后它的值为:
例如b=-128[1000 0000] c=1[0000 0001]。
位向量相减得 b-c=[0111 1111] ,127。
公式10计算结果,根据公式11情况3, b−c+2w=−128−1+256=127 。
用取模运算计算乘法溢出后的值:
设一个w位的有符号整数变量a,它储存w位的有符号整数变量b和c的积,则:
–用移位替代乘法和除法
1,左移替代乘法
无符号编码中的公式5对补码编码依然通用,既:
结论: 有符号整数x∗2k 等于在位向量的低位添加k个0 ,也既是整个位向量向左移k位。
2,右移代替除以2的幂
在无符号编码中,代替除法的右移为逻辑右移,既在最高位添加0。
对于一个最高位负权为0的有符号整数,逻辑右移的效果是一定与无符号整数一致的。但是如果最高位负权为1,最高位添加0肯定是错误的,负数将会变为正数。因此替代的方法为算术右移。设x为有符号整数变量x,整数k为
0≤k<w
,
x,
为截取x w-k位的有符号整数,
x”
为截取x低k位的无符号整数,用无符号编码部分相同的逻辑可以推理到公式6-5,
x=2kx,+x″(公式6−5)
,两边除以
2k
得
x/2k=x,+x″/2k
,但是再进一步分析就要考虑x为负数的情况了:
1,x为正数
x,
为x移位前左边部分截取的位向量,它的值是我们算术右移后应该得到的值,它的负权位与x的负权位是一致的,
x″
为x的位向量的低k位的无符号数,设为无符号数是因为它代表的是x移位前低k位的值。
x″/2k
一定是一个小于1的小数,那么可以把
x=2kx,+x″(公式6−5)
两边同时向下舍入得出
⌞x/2k⌟=x,(公式6−7)
,此公式的意义既是有符号整数x除以
2k
等于算术右移k位。这些内容等同于无符号编码的部分。
2,x为负数
x,
为x移位前左边部分截取的位向量,它的值是我们算术右移后应该得到的值,它的负权位与x的负权位是一致的,
x″
为x的位向量的低k位的无符号数, 设为无符号数是因为它代表的是x移位前低k位的值。公式6-5两边除以
2k
得
x/2k=x,+x″/2k
,现在,
2k
一定为正数,如果x为负数,那么
x/2k
为一个负数,由于我们想要的实现的整数除法的结果是舍去小数部分的整数,一个负数向下舍入将会改变整数部分,例如-3.14向下舍入将会得到-4,这里应该向上舍入得-3。但是实际上代码的整除除法的结果总是向下舍入,因此须找到一个能达到
⌜x/2k⌝
效果的某种向下舍入的方法。
设
x=k∗2k+r,0≤r<2k
,k代表x除以2^k后的整数部分, r代表x除以
2k
后的余数部分。
两边同时加2^k-1再除以2^k并向下舍入:
当r=0时,既x没有小数时, ⌞2k−12k⌟ 一定等于0,公式13左边部分等于x的整数部分。
当r>0时,由于 r<2k,⌞r+2k−12k⌟=⌞2k2k⌟+⌞r−12k⌟=1+0=1 。公式13左边部分等于x减去小数部分,整数部分加一。
因此 ⌞(x+2k−1)2k⌟ 既是我们想要的转换 ⌜x/2k⌝ 的方法。
将 2k 转换成移位, 2k=1∗2k=1<<k 。
3,结论:对于有符号整数变量x,除以 2k ,等同于对它的位向量 [xw−1,xw−2,...,x0] 进行以下的移位操作:
–有符号数和无符号数之间的转换
设一个无符号数变量x,它的位向量是 [1111 1111],它的值为255。
将变量x强制转换为有符号格式,它的位向量不发生任何改变,依然还是[1111 1111],在补码编码下,它的值为-1。
考虑一下它的值的变化,在无符号编码下,它的值为
23+22+21+20
,在补码编码下,它的值为
−23+22+21+20
,所以x在由无符号变量转换为有符号变量的过程中,它的值减少了两个
23
,反过来讲既是,x在由有符号变量转换为无符号变量的过程中,它的值增加了两个
23
,如果设x的位向量为w位,
[1w−11w−21110]
,两个
23
既是两个
2w−1
,两个
23
等于
24
,两个
2w−1
等于
2w
。将这个变换转为数学公式:
在由无符号变量转变为有符号变量后,当x的最高位,w-1位,负权位为1时,它自身的值会增加 xw−12w ,当他的最高位w-1为0时,它的值 保持不变。反之亦然。
ASCII字符
假设有位向量 [0100 0001]
通过无符号编码解释,此向量代表65。
通过补码编码解释,此向量代表65。
通过ASCII字符码解释,此向量既是代表 “A”。
因此在C语言中,将一个被赋值“A”的char变量用%d 打印出来的结果将会为“65”。
ASCII码表中的127个打印字符分别对应[0000 0000]至[0111 111]的位向量。由于不涉及到最高有效位,所以在c语言中声明一个 char a=”A”;不管是用%d还是%u格式打印,输出都会是65。
附:
十六进制Hexadecimal/十进制Decimal/二进制Binary(无符号编码) 对应表(16进制0至F):
Hex | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C |
Bin | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 |
Dec | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
Hex | D | E | F | ||||||||||
Bin | 1101 | 1110 | 1111 | ||||||||||
Dec | 13 | 14 | 15 |
Reference:
深入理解计算机系统(原书第二版 )—Randal E.Bryant
维护日志:
2017-8-22:
-更改一处错误:将“a的取值范围为
−2w−1≤a≤2w−1
”改为将“a的取值范围为
−2w−1≤a≤2w−1−1
”。
-修正了最后对应表中E,F没显示出来的问题。