CSAPP实验1——DataLab实验报告

bitXor

思路

如果 x y 相同, (~x&~y) (x&y) 一定是完全相反的,那么 & 操作就会返回 0. 在其他情况下,在 ~(~x&~y) ~
(x&y) 中一定会有重叠的 1

核心代码

(~(~x&~y)&~(x&y))

thirdBits

思路

要求生成 3 位一个 1 的掩码。最一开始想到的是将初始的 8 位掩码 0x49 每次向前移动 3 4 的公倍数 12 ,后来发现
只需要每次向前移动 9 位,多余的 0 通过移位产生

核心代码

int x = 0x49 ;
int s = x<< 9 ;
int m = x+s;
int ans = (m<< 18 )+m;

fitsShort

思路

如果要判断某个数是否可以用 16 位表示,只要判断前 17 位是否为全 0 或全 1 即可
核心代码
x=x>> 15 ;
return !((x>> 16 )^x);

isMax

思路

tmax+tmax+2 = 0
注意: 0xffffffff 也满足 x+x+2=0 的条件,此时需要找到一个只有 0xffffffff 表达式为 0 其余都不为 0 的情况,即 x+1

核心代码

int m = x+1 return !((m+m)|!m)

fitsBits

思路

判断 x 是否可以用 n 位表示。思路与 fitsShort 相似,若前 33-n 位全为 0 1 ,则可以用 n-bit 表示
细节:
~n = -n-1
先把最后 n 位移到最前面,再移回原位,这样就做到把最前面 32-n 位清零或变 1

核心代码

int t = 33 +~n;
int a = !(((x<<t)>>t)^x);
return a;

upperBits

思路

要求生成前 n 位都是 1 的掩码
本想直接将 0x80000000 后移 n-1 位,发现 n=0 时要移动 -1 位,无法操作。所以特殊化 n=0 的情况
优化:
n+(~0)=n-1 ,优于 n+(~1)+1

核心代码

int b = n+(~ 0 ); //b=n-1
int mask = ((!(!n))<< 31 )b;
// 0:0xffffffff;0:0
// 这一步非常关键,把非 0 0 的操作区别开来

anyOddBit

思路

要求判断奇数位是否有 1 ,通过移位生成只有奇数位有 1 的掩码并 &x
第一次写的问题出在: mask&x 是一个数字,不是 1 ,所以!!一下

核心代码

int a = mask&x; // 如果奇数位有 1 ,则 a 不是 0 ,否则 a 0
return !!a;

byteSwap

思路

利用亦或运算重要性质: a^(a^b)=b; b^(a^b)=a
把需要交换的位数移动到最低 8 位并进行亦或,然后将亦或后的部分移动到原来的位置,再次进行亦或就可以进行交换
细节: n*2^m = n<<m

核心代码

int a = x>>n;
int b = x>>m; // 把需要交换的位数移动到最低 8
int axorb = (a^b)&mask; // 得到最低 8 位的 a^b
int ans = x^((axorb<<n)|(axorb<<m)); //x^0=x,a^(a^b)=b; b^(a^b)=a
return ans;

absVal

思路

先判断是否为负数,若为负数,取反加一
当函数对正数和负数的操作不同时,需要先用 x>>31 判断正负
x^1 取反, x^0 不变,可以用来对正负数区分操作

核心代码

int a = x>> 31 ; //x>0,a=00...0;x<0,a=11...1
int b = (x^a)+(a& 1 ); // 补码转真值:取反加一
return b;

divpwr2

思路

要求计算 x/(2^n) ,通过右移 n 位得到 x/(2^n)
注意: round to 0, 因为正数和负数 round to 0 时进行的操作不同,所以负数需要加上 mask :把低于 n 位的数字置为1

核心代码

int N = 1 <<n;
int judge = x>> 31 ;
int mask = judge&(N+(~ 0 ));
return (x+mask)>>n;

float_neg

思路

首先判断是否为 NaN-> 阶码位是否全为 1 且小数位不全为 0
若是 NaN-> 直接返回 uf
若不是 NaN-> 相反数且不是 0 :符号位取反
优化:我本来想的是通过移位操作分别判断 exp 是否全为 1 frac 是否不全为 0 ,但是在上了周日的课程之后忽然 醒悟,可以把小数当成二进制整数去理解。exp 全为 0 frac 不全为 0 用二进制理解就是 uf>0x7f800000

核心代码

if (xsign> 0x7F800000 )
return uf;
ans = uf^ 0x80000000 ;

logicalNeg

思路

1+1=0 ,全 0+0=1 ,所以只需要把非 0 转化为全 1 ,全 0 依旧是全 0 就可以了:把非 0 的首位变为 1 然后移位
- 正数 = 负数, -0=0 :通过正负数取或来将非 0 的首位变为 1 同时 0 不变
注意:
如何获得全 1 ~0 -1 ~1+1
最后要返回一个数值 1 0 ,因此不能单纯地 & 一个全 1

核心代码

int a = ((x|(~x+1))>>31)+1;

bitMask

思路

要求:将 mask lowbit highbit 都置为 1
考虑两种情况: lowbit<highbit/lowbit>highbit
具体操作:把 [highbit,32] [0,lowbit-1] 置为 0
注意:如果 highbit 31 :从 a<<(highbit+1) 改为 a+(1<<highbit<<1). 用加法替换 << ( 后直接用 2<<highbit 替换)

核心代码

int a = ~ 0 ; // 生成全 1
int ans = (a<<lowbit)&(a+( 2 <<highbit));

isGreater

思路

y-x<0->x>y
注意: tmin 的问题: tmin 取反加一还是它本身 , 所以要加上对于 xy 的正负号判断

核心代码

int a = y+~x+ 1 ;
return (sign_a& 1 )^(!(sign_a^sign_x)&(sign_a^sign_y));

logicalShift

思路

要求强制进行逻辑移位。思路是生成一个前 n 位为 0 的掩码,清空算术移位造成的 1
注意:生成掩码时,先移动再取反,不然左移最低位会变成 0

核心代码

int m = 0x1 << 31 >>n<< 1 ; // 先移动再取反,不然左移最低位会变成 0
int mask = ~m; //mask = 0...0111...1
int x_n = x>>n; //x 进行算术移位
int ans = x_n&mask; // 把算术移位造成的 1 清空

satMul2

思路

考虑什么情况下会溢出:最高位与次高位不同
没有 if 如何进行分支:预先算出两种情况需要的操作然后用 c& + ~c& 使得进行一种操作的时候另一种为 0
注意:溢出时出现的情况是不确定的,尽量不使用溢出对数据进行操作

核心代码

int a = x<< 1 ; // 正常:直接乘 2; 溢出:顺便获取第 2
int b = a>> 31 ; // 正数: b=1111; 负数: b=0000
int c = (x^a)>> 31 ; // 溢出: c=1111; 正常: c=0000;
int tmin = 1 << 31 ; //tmin=1000
int ans = ((~c)&a)|(c&(tmin^b)); // 正数 :b^tmin=0111(tmax); 负数: b^tmin=1000(tmin)
// 如果不溢出就走前面的,溢出就走后面的;正数与负数的溢出通过 b^tmin 来控制

subOK

思路

负数减正数溢出:差大于 0 ;正数减负数溢出:差小于 0
只有异号数相减才可能溢出

核心代码

int diff = x+(~y)+ 1 ;
int ans = !((sign_x^sign_y)&(sign_x^sign_d));

trueThreeFourths

思路

先除以四再乘三,虽然这样会丢失精度,但为了防止溢出不得不出此下策
注意:不能用 1/2+1/4=3/4 。这个式子虽然在数学上是成立的,但是在舍入的时候会造成错误
round to 0 的操作:正负数操作不同:正数向下取整,负数向上取整。向下取整其实不用操作,但是向上取整需要在除四前加三

核心代码

int f = x& 0x3 ;
int sign = (x>> 31 )& 0x3 ;
int frac = (f+f+f+sign)>> 2 ;
return temp + frac;

isPower2

思路

如果是 2 的倍数,则 x 只有一个 1 ,即 x&(x-1)=0
特殊情况:只有符号位为 1 的负数和全 0

核心代码

int a = x&(x+(~ 0 )); //ispower2,Tmin,0->0
int sign = x>> 31 ; // 正数: 0 ;负数:全 1
int iszero = !x; // 0 1 ;非全 0 0
int ans = a|sign|iszero; // 只有 ispower2 时这三者全是 0
return !ans;

float_i2f

思路

float x=(-1)^signbit * 2^(exp-Bias)(1+frac)
frac :丢弃头 1 并在尾部补 0
向偶舍入 : 首先 32-23=9 ,所以只有最后 9 位需要舍入:后 9 >100000000, 显然进位 ; 9 =100000000, 则判断第
10 位是否为 1
特殊情况: 0 不参与浮点数表示,直接返回 0

核心代码

int exp = 158 -calzero;
int flag = 0 ;
if ((temp& 0x1ff )> 0x100 )
flag = 1 ;
if ((temp& 0x3ff )== 0x300 )
flag = 1 ;
unsigned int ans = (sign<< 31 )+( exp << 23 )+(temp>> 9 )+flag;

howManyBits

思路

先测前 16 位有没有数字,若有,右移 16 位,在前 16 位中操作找出具体有多少 ; 若无,考虑后十六位中的前 8
特殊情况: 0 也需要 1 位(用或来实现) , 负数只需要前面有一个 1

核心代码

int temp = x^(x<< 1 ); // 第一个有效数字前面一定全是 1 或者 0 ,所以抑或一下把第一个有效数
字之前的位数置空
int move_16 = !(!(temp>> 16 ))<< 4 ; // 如果前 16 位不为 0 ,则 move16, 否则 move0
...
temp = move_16+move_8+move_4+move_2+move_1+ 1 ; // 除非全是 0 不然 move_1 肯定有一个 1
加上的 1 是符号位

float_half

思路

首先,判断 NaN. 另外,虽然都是 uf ,但是还是需要留出符号位
0.5f=f*1/2=exp-1
但也有一种可能, exp 太小了,乘以 1/2 就溢出了,此时的操作:负数:返回 tmin; 正数:对 frac 进行操作:将 frac乘以1/2

核心代码

unsigned int sign = uf& 0x80000000 ; // 获取符号位
unsigned int exp = uf& 0x7f800000 ; // 获取阶码位
int a = ((uf& 3 )== 3 ); // 判断 uf 的最后两位是不是 11 ,如果是则 a=1 (用来 round to 0)
if ( exp == 0x7f800000 ) // 如果阶码位全 1 ,返回本身
return uf;
if ( exp <= 0x800000 ) //exp 太小:负数:返回 tmin; 正数:对 frac 进行操作:将 frac 乘以 1/2
return sign|(((uf^sign)+a)>> 1 );
if ( exp )
return uf -0x800000 ; // 正常情况, exp-1
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值