《深入了解计算机系统》家庭作业2.73、2.74
2.73
写出具有以下原型的函数代码:
/*Addition that saturates toTMin or TMax */
int saturating_add(int x, int y);
同正常的补码加法的溢出的方式不同,当正溢出时,饱和加法返回TMax,负溢出时,返回TMIn,饱和运算常常用在执行数字信号处理的程序中。
你的函数应该遵循位级整数编码规则。
先捋一捋需求:
1、若发生正溢出:返回TMax;
2、若发生正溢出:返回TMin;
3、题目虽然没说(或者我理解有误),但若没有发生溢出时也应该有个返回值,返回0;
再捋一捋规律:
1、补码相加发生正溢出时,两个参数一定都大于0;
2、补码相加发生负溢出时,两个参数一定都小于0;
3、判断溢出的方法是,当两个参数的符号符合溢出条件时,检测其相加的结果符号是否发生改变;
再捋一捋思路:
为了方便描述,定义一个参数:int result = x + y ;
1、规律1和2是发生溢出的必要条件,可以通过算术右移的操作生成只保留符号信息的二进制码——0x0(全0)或全1,然后将两个参数的符号信息按位异或(XOR),若符号相同(即满足了发生溢出的条件),异或操作后生成的二进制码会是0x0;到这里,再对结果取非,因为发生溢出的条件是溢出的必要条件(好废话啊= =!),这是为了让这个二进制码能通过按位与的操作左右最终的结果。
2、规律3是发生溢出的另一个条件,随便取一个参数,同样通过算术右移的操作只保留符号信息,然后对result同样进行算术右移的操作以获取符号信息,再将结果按位异或(XOR),若符号发生改变,会返回全1。
3、根据result的符号信息决定返回TMin还是TMax,我的思路是用result的符号信息(全0或全1)减去TMin,发生溢出时有以下几种情况:
溢出的情况 | result算术右移后的结果 | 我想要的结果 | result符号信息减去TMin |
---|---|---|---|
正溢出 | 全1 | TMax(最高位是0,其余全1) | TMax |
负溢出 | 全0 | TMin(最高位是1,其余全0) | TMin |
可以看到通过符号信息二进制码减去TMin的操作可以得到预期的结果
4、将1、2、3的结果相与,得到最终的返回值;
OK,废话完毕,Talk is cheap,Show me the code:
int saturating_add(int x,int y){
int result = x+y;//定义变量result,存储x+y的结果
int bias =(sizeof(int)<<3)-1;//获取算术右移的偏移量
int condition1 = ~(x>>bias^y>>bias);//定义变量condtion,存储发生溢出的必要条件(思路1、规律1、2)
int result_signed = result>>bias;//同样只保留符号信息
int condition2 = result_signed^(x>>bias);//与任意一个参数相异或,若result是发生溢出的结果,则得到全1的二进制码
int TMin = 0x1<<bias;//通过左移位操作得到TMin
int result_overflow = result_signed-TMin;//思路3
return result_overflow & condition1 & condition2;//三个结果相与,得到最终结果
}
测试代码
(为了偷懒,我把所有家庭作业写进了同一个源文件,测试的时候用的同一个main()函数,所以我直接贴图了):
1、两正相加不溢出
2、两负相加不溢出
3、负溢出
4、正溢出
2.74
写出具有以下原型的函数代码:
/Determine whether arguments can be subtracted without
overflow/
int tsub_ok(int x,int y);
如果计算x-y不溢出,这个函数就返回1。
需求:
1、两个参数相减,不溢出返回1.
规律:
1、两个参数相减,符号不同一定不溢出
2、两个参数相减,溢出的结果符号一定改变
3、负数被减只能正溢出,正数被减只能负溢出
思路:
1、参照2.73的经验,同样对参数进行算术右移只保留符号信息,然后相异或,只有异或的结果若为0,才满足溢出的条件,所以再对异或的结果取非,最后的二进制码为全1的时候才满足溢出的条件
2、根据思路3,对相减的结果result进行同样的算术右移操作,然后对被减数y也进行同样的算术右移操作,保留符号位,两者相异或,若返回0,则说明发生了溢出。
x的符号 | y的符号 | result的符号 | 溢出/不溢出 |
---|---|---|---|
1 | 1 | 0或1 | 不溢出 |
1 | 0 | 1 | 不溢出 |
1 | 0 | 0 | 负溢出 |
0 | 1 | 1 | 正溢出 |
0 | 1 | 0 | 不溢出 |
0 | 0 | 0或1 | 不溢出 |
The code:
int tsub_ok(int x,int y){
int result =x-y;
int bias = (sizeof(int)<<3)-1;
x = x >> bias;
y = y >> bias;
result = result >> bias;
int condition1 = x^y;//若值为全1,才可能发生溢出
int condition2 = ~(result ^ y);//溢出值为全1,不溢出返回0;
return (condition1&condition2)==0;//两个条件有一个不满足,就判定没有发生溢出。
}
测试代码
1、最极端的情况,TMin-TMin:
2、两正相减(一定不溢出):
3、两负相减(一定不溢出):
4、正负相减(结果预期:不溢出/溢出):
5、负正相减(结果预期:(不溢出/溢出)):
踩坑:做2.74的时候,我的第一个方案是直接对y取负值(即令y = - y;),然后再与x相加(result = x+y;),就可以套用2.73的部分代码来操作,但是当y值为TMin时,没有对应的-y,所以行不通.
这是我写的第一篇博客,一个人看书敲代码很无聊,所以试着把学习的心得写成博客分享出来。
最后,本人非相关专业从业者,代码与思路有拖沓冗余之处,请多多指教,感激不尽。