有符号整数的机器级表示与算术运算(没写完)

有符号整数的机器级表示

使用二进制位表示能带负号的有符号整数时,需要额外占用1个位来表示正负。有如下3种公认的表示方案:

1) \(k\)位原码:最高的第\(k\)位表示符号,称为符号位。0表示正数,1表示负数。剩下的位称为数值位,按\(k-1\)位无符号整数的方式表示绝对值。注意在原码下0有两个码,比如在4位下0有0000和1000的2种表示。这时会定义前者为负的-0,后者为正的+0(也就是0分为正数和负数两个)。\(k\)位原码的取值范围是\([ -(2^{k-1}-1), 2^{k-1}-1 ]\);

2) \(k\)位反码:正数(含+0)为其原码,负数(含-0)为其原码“符号位不变,其他位取反”。反码取值范围和原码一样;

3) \(k\)位补码:正数(含+0)为其原码,负数(含-0)为其反码+1。补码中的-0会溢出,比如4位反码1111的原码会溢出,使其变为0000,也就是-0和+0一样,所以补码无需区分-0与+0。这里还有个问题是补码1000的意义不明确,在补码中会直接规定其表示-8。故4位补码的能表示-8~7,其中只有1个补码表示0,一般的\(k\)位补码的取值范围是\([ -2^{k-1}, 2^{k-1}-1 ]\);

历史上的计算机曾使用原码或反码直接表示有符号整数,补码虽然相对抽象,但补码的性质更适合计算机,所以目前计算机大都用补码表示有符号整数。之后默认有符号整数=补码。

 

无符号整数码与补码

上面补码的定义比较复杂,尤其是100…00对是直接定义的,因为该定义强调的是补码如何从原码演化而来的。为了更好的理解补码,可以分析得到补码和无符号整数码之间的一些关系:

a) 设\(a\)在\(k\)位补码表示范围内,\(a\)的\(k\)位补码与\(a ~mod~ 2^k\)的\(k\)位无符号整数码相同;

证明:
1) \(a\geq 0\):这时\(a ~mod~ 2^k = a\),\(a\)的\(k\)位无符号整数码和补码一样,符合原始定义;
2) \(a<0, a\neq -2^{k-1}\):根据原始定义,这时补码来源于\(-a\)的\(k\)位原码(也是\(-a\)的\(k\)位无符号整数码)的“按位取反+1”。又根据之前对无符号整数码“按位取反+1”的讨论,“按位取反+1”的结果看作\(k\)位无符号整数码时表示\((-(-a)) ~mod~ 2^k\)
3) \(a=-2^{k-1}\):根据原始定义其补码是10…0,看作\(k\)位无符号整数码表示\(2^{k-1} = (2^k – 2^{k-1}) ~mod~ 2^k \)。根据余数性质,其等于\((- 2^{k-1}) ~mod~ 2^k\)。该补码看作\(k\)位无符号整数码时表示\((- 2^{k-1}) ~mod~ 2^k\);

b) 设\(a\)在\(k\)位补码表示范围内,\(Y_i\in(0,1)\)为\(a\)的\(k\)位补码第\(i\)位(0开始),有\(a=-2^{k-1}Y_{k-1} + \sum_{i=0}^{k-2} 2^iY_i\);

证明:
1) \(a\geq 0\):其\(k\)位补码等于其无符号整数码,所以有\(a = \sum_{i=0}^{k-1} 2^iY_i \),此时\(Y_{k-1}=0\),所以上式成立;
2) \(a< 0\):根据上述(a)其\(k\)位补码等于\(a~mod~2^k\)的\(k\)位无符号整数码,所以有\(a~mod~2^k = \sum_{i=0}^{k-1} 2^iY_i \)。考虑到\(a\)的取值范围,有\(a~mod~2^k = 2^k + a\)。整理可以得到\(a = -2^k + \sum_{i=0}^{k-1} 2^iY_i\),此时\(Y_{k-1}=1\),所以上式成立;

上述(a)使得很多时候能直接用“为无符号整数设计的硬件”实现补码运算,为更好的理解补码,本文先讨论如何用“为无符号整数设计的硬件”实现补码的运算,然后再讨论补码专用运算器。

 

补码的符号扩展

符号扩展是二进制位上的一种操作,将\(k\)位数据符号扩展成\(n(n>k)\)位数据是“将原本的\(k\)位作为新数据的低\(k\)位。若原本的最高位为1,则新数据的高\(n-k\)位全部填1,否则全部填0”。比如说把4位二进制码1011符号扩展为6位是111011。符号扩展在用于补码时,其意义非常明确:

c) 设\(a\)在\(k\)位补码取值范围内,将\(a\)的\(k\)位补码符号扩展为\(n(n>k)\)位,把该结果看作\(n\)位补码,其表示的还是\(a\);

证明:\(a\geq 0\)时,这个结论显然正确,所以只需讨论\(a<0\)的情况。根据上述补码性质,\(a<0\)的\(k\)位补码在被看作\(k\)位无符号整数码时表示\(a~mod~2^k\),将该码符号扩展到\(n\)位后可看作\(((a~mod~2^k) + 2^k + 2^{k+1}…2^{n-1})~mod~2^n\)的\(n\)位无符号整数码。通过等比数列求和公式,上式等于\(((a~mod~2^k) + (2^n – 2^k))~mod~2^n\)。由于\(a<0\)且\(a\in [-2^{k-1},2^{k-1}-1]\),可去掉1个\(mod\),上式等于\(((2^k + a) + (2^n – 2^k))~mod~2^n = a~mod~2^n\)。显然\(a\)也在\(n\)位补码表示范围内,所以根据上述补码性质,\(a\)的\(n\)位补码可看作\(a~mod~2^n\)的\(n\)位无符号整数码,即符号扩展的结果。

符号扩展这种操作在计算机中经常使用,尤其是对补码。另外还有一种扩展称为无符号扩展(零扩展)。其更多的被用于无符号整数码,规则十分简单,即原来的位不变,多出来的高位补0。实现这2种扩展的电路不复杂,这里不讨论。

 

补码的加法

设\(a,b,a+b\)在\(k\)位补码取值范围内,将\(a,b\)的\(k\)位补码做“无符号整数码的加法”。结果可看作\(a+b\)的\(k\)位补码。

证明:把\(a,b\)的补码看作\(k\)位无符号整数码,其表示的值分别是\(a ~mod~ 2^k\)和\(b ~mod~ 2^k\)。把加法器的输出再看作\(k\)位无符号整数码,其表示\(((a ~mod~ 2^k)+(b ~mod~ 2^k)) ~mod~2^k\)。根据余数加法定理其等于\((a+b)~mod~ 2^k\)。由于\(a+b\)在\(k\)位补码取值范围内,根据上述补码性质,加法器输出可看作是\(a+b\)的\(k\)位补码。

注意如果\(a,b\)在\(k\)位补码取值范围内,但\(a+b\)不在。那么会因为溢出导致“负+负得正”和“正+正得负”的情况,当然“正+负”,“正+0”,“负+0”都不会导致\(a+b\)超出取值范围。

 

补码的减法

设\(a,b,a-b\)在\(k\)位补码取值范围内,将\(a,b\)的\(k\)位补码做“无符号整数码的减法”。结果可看作\(a-b\)的\(k\)位补码。

证明:把\(a,b\)的补码看作\(k\)位无符号整数码,其表示的值分别是\(a ~mod~ 2^k\)和\(b ~mod~ 2^k\)。把“减法”结果再看作\(k\)位无符号整数码,其表示\(((a ~mod~ 2^k)-(b ~mod~ 2^k)) ~mod~2^k\)。根据余数加法定理其等于\((a-b)~mod~ 2^k\)。由于\(a-b\)在\(k\)位补码取值范围内,根据上述补码性质,“减法”结果可看作是\(a-b\)的\(k\)位补码。

证明过程里直接把补码当作无符号整数码,所以不用像讨论无符号整数那样,先讨论补码的“按位取反+1”。

 

补码的乘法

e94e42d9-767d-45ba-9e74-f0ae9b4492a0

设\(a,b,ab\)在\(k\)位补码取值范围内,将\(a,b\)的\(k\)位补码做“保留\(k\)位积的无符号整数码的乘法”。结果可看作\(ab\)的\(k\)位补码。

证明:把\(a,b\)的补码看作\(k\)位无符号整数码,其表示的值分别是\(a ~mod~ 2^k\)和\(b ~mod~ 2^k\)。把“乘法”结果再看作\(k\)位无符号整数码,其表示\(((a ~mod~ 2^k)(b ~mod~ 2^k)) ~mod~2^k\)。根据余数乘法定理其等于\((ab)~mod~ 2^k\)。由于\(ab\)在\(k\)位补码取值范围内,根据上述补码性质,“乘法”结果可看作是\(ab\)的\(k\)位补码。

前面讨论的保留\(2k\)位积的乘法不能用于补码。简单分析下,先把\(a,b\)的补码看作\(k\)位无符号整数码,其表示的还是\(a ~mod~ 2^k\)和\(b ~mod~ 2^k\)。由于保留\(2k\)位积的乘法不会溢出,把积看作\(2k\)位无符号整数码,其表示的是\((a ~mod~ 2^k)(b ~mod~ 2^k)\)。可以将其写成\(((a ~mod~ 2^k)(b ~mod~ 2^k)) ~mod~ 2^{2k}\)。到这里就可以发现问题,就是这个结果不一定能看做是\(ab\)的\(2k\)位补码。而且这个结果甚至不一定能看做\((a ~mod~ 2^k)(b ~mod~ 2^k)\)的\(2k\)位补码,因为\((a ~mod~ 2^k)(b ~mod~ 2^k)\)还可能会超出\(2k\)位补码的取值范围,这里举个例子:“设乘数与被乘数为3位,积为6位,有“\(111\times 101 = 100011\)”。看作无符号整数码时该式表示\(7\times 5 = 35\)。看作补码时表示\((-1)\times (-3) = -29\)。补码结果显然是错误的,其甚至不能表示\((a ~mod~ 2^k)(b ~mod~ 2^k) ~mod~ 2^{2k}\),因为带入后等于\(((-1) ~mod~ 8)((-3)~mod~8) ~mod~ 64 = 35\)”。

想要让补码也支持保留\(2k\)位积的乘法,需对其原本步骤做一点点改变。有一种方式是加入符号扩展操作,在最开始把\(k\)位被乘数存入\(2k\)位寄存器时做一次符号扩展,把\(k\)位被乘数存入的寄存器也换成\(2k\)位的,并在一开始存入时也作一次符号扩展。这样处理其实等价于做上面的那种保留\(k\)位积的乘法,所以单纯这样做的意义不是很大。

 

补码的BOOTH乘法

移位相加法中,乘数中1的数目决定加法次数,而加法次数很影响性能,所以有这种优化思路,比如对8位的01111110补码,如将其作为10000000 – 00000010,那么1的数目从6个降低为2个。这种优化思路被用于BOOTH乘法。其中最基本的一种是基2(radix-2)BOOTH乘法,根据上述补码性质(b),如规定\(Y_{-1} = 0\),则\(a = \sum_{i=0}^{k-1} 2^i(Y_{i-1}-Y_i)\)。由于\(Y_{i-1}-Y_i\)只能是0,1,-1,所以可认为该乘法是扩展了支持-1的“乘法竖式”,上图右边就是其对应的“竖式”。更常用的BOOTH乘法是基4BOOTH乘法,\(a\)还能写成\(2^{k-2}(-2Y_{k-1} + Y_{k-2} + Y_{k-3})+2^{k-4}(-2Y_{k-3} + Y_{k-4} + Y_{k-5})…(-2Y_1+Y_0+Y_{-1})\),其中每项可取0,1,-1,2,-2,其中2,-2可通过左移1次完成,所以基4和基2的BOOTH乘法一样,只需要加法、减法、移位就能完成。直观上看基2BOOTH乘法是把相邻的两个1变成0,基4BOOTH乘法是把相邻的3个1变成0。由于BOOTH乘法是基于补码性质(b)的,所以BOOTH乘法无法直接用于无符号整数码。另外,这里只讨论BOOTH乘法核心思路,而不去分析逻辑细节或证明正确性,比如“11111111乘-1溢出会不会有什么影响?”等等细节问题。并且也不讨论BOOTH乘法的电路实现。

 

补码的除法

补码不能像加减乘那样照搬无符号整数码的运算规则,一是因为加减乘的正确性都是通过余数加法和乘法定理证明的,但没有同样的除法定理,二是由于补码带有符号,不能直接用原来的办法判断“是否够减”。另外前面也提到过,计算机处理余数和数学处理余数的规范不太一样。总之补码要重新设计除法逻辑,下面讨论补码专用补码恢复余数法

这里以\(k\)位被除数除数为例。其使用\(2k\)位的寄存器A和\(k\)位的B,\(2k\)位的加法器。看具体流程:

1) B存除数,A存符号扩展到\(2k\)位补码的被除数;

2) 循环执行\(k\)次如下步骤:

2.1) 将A整体逻辑左移1次;

2.2) 若A高\(k\)位与B符号相同,则做(A高\(k\)位)=(A高\(k\)位)-B,否则做(A高\(k\)位)=(A高\(k\)位)+B;

2.3) 若(2.2)前后A高\(k\)位未变号,或当前等于0,说明“够减”,把A低\(k\)位的最低位设为1。否则说明“不够减”,需要做1次恢复余数,若(2.2)做的是加法,则做(A高\(k\)位)=(A高\(k\)位)+B,否则做(A高\(k\)位)=(A高\(k\)位)-B;

3) 循环结束后,A的高\(k\)位为余数,若原被除数与除数同号,则A低\(k\)位为,否则A低\(k\)位的“按位取反+1”为商;

可以画图分析4位补码下\((-7) \div 3\)的过程,最后得到的商是-2,余数是-1。所以这种除法没有遵循数学中整数带余除法定理所定义的除法,但满足\(商\times 除数 + 余数 = 被除数, |余数|<|除数|\)。补码恢复余数法其实是根据被除数和除数的符号分为两类情况处理的,在被除数和除数符号相同时,补码与无符号整数码的恢复余数法相同,当两者异号时,则通过不停的加上“移位后的除数”,直到被除数被加的十分接近0。这里不讨论其逻辑细节,不去具体证明其正确性。

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部