文章目录
CS:APP Lab1:datalab
一.bitXor(x,y)
puzzle分析:
本题要求只用 & 和 ~ 两种操作来实现或运算.或运算可通过代码return (x&~y)|(~x&y)
来实现,为避免使用 | 操作,可通过将上式进行变式:
(x&~y)|(~x&y)=~((~x|y)&(x|~y))=~(((~x|y)&x)|((~x|y)&~y))=~((x&y)|(~x&~y))=~(~x&~y)&~(x&y)
就可得到如下实现代码.
实现代码:
int bitXor(int x, int y) {
return ~(~x&~y)&~(x&y);
}
二.tmin
puzzle分析:
本题要求通过使用位运算符来求得32位机器最小整数值.易知32位机器表示最小补码的是-231,即符号位为1,后跟31个0.这个运算可以用左移运算实现,即1<<31
.
实现代码:
int tmin(void) {
return (1<<31);
}
三.isTmax(x)
puzzle分析:
本题为判断一整数x是否为最大二进制补码,可根据当最大补码(符号位是0,后跟31个1)加一后会溢出为最小补码,即1后跟31个0,二者各位上数字刚好互补的性质,可通过代码!~(x+(x+1))
将二者相加并取反即得到0再求非即得所求的1.但是要注意当x为-1即各位都是1时,加1也会溢出成0,经过上述运算同样得到1的结果,因此得排除-1的输入.因此只需利用!(x+1)=0(当仅当x不等于-1时成立)
来为~(x+(x+1))
加上偏移即可.
实现代码:
int isTmax(int x) {
return !(~(x+(x+1))+!(x+1));
}
四.allOddBits(x)
puzzle分析:
本题要求判断当输入的二进制数x各奇数位都是1时返回1,否则返回0.容易得到:二进制数各奇数位都为1、偶数位都为0时,每4bit位数字分布应为1010(考虑有第0位),转换成16进制就是A,因此奇数位全为1、偶数位全为0的二进制数转换为16进制数就是0xAAAAAAAA.因此只需将x与0xAAAAAAAA进行比较:按位进行"与运算",所得结果在奇数位上数字情况与x一致,偶数位都是0,将这个结果再与0xAAAAAAAA进行位"异或运算",如果每一位都一样,将得到0,取非就是1.(注:由于定义变量只能使用8bit大小的整数,因此要通过移位运算得到整数0xAAAAAAAA.
实现代码:
int allOddBits(int x) {
int val = 0xAA + (0xAA<<8);//得到0xAAAA
val = val + (val<<16);//得到0xAAAAAAAA
return !((x&val)^val);
}
五.negate(x)
puzzle分析:
本题要求某整数的负数,按照定义可知,按位取反再加1即得.
实现代码:
int negate(int x) {
return ~x+1;
}
六.isAsciDigit(x)
puzzle分析:
本题要求判断某十六进制数是否在asc码中对应’1’~‘9’.思路:用该数减去0x30,得到非负数(符号位为1);减去0x3a,得到负数(符号位为1),就可判断该数属于这个区间.然而不允许使用符号’-',则可利用上题求负数的思路进行减法运算.再将分别所得结果右移31位得到各自的符号位,然后将之分别与1进行"与运算",再处理并合并即得最终返回结果.
实现代码:
int isAsciiDigit(int x) {
int a = x+(~0x30+1);
int b = x+(~0x3A+1);
a = (a>>31)&0x1;
b = (b>>31)&0x1;
return !a&b;
}
七.conditional
puzzle分析:
本题要求使用给定的运算符实现条件表达式的功能.首先用!!x
将输入的x转化位布尔量0或1,取负数得到各位全为1或全为0,再创一个与x有关的参数的反码,分别将二者与输入的y,z进行"与运算"再进行"或运算"合并即可选择性舍弃掉不需要的值.
实现代码:
int conditional(int x, int y, int z) {
int a = ~(!!x)+1;//得到各位都为1或各位都为0
int b = ~a;//取反
return (y&a)|(z&b);
}
八. isLessOrEqual(x, y)
puzzle分析:
本题要求判断某数是否小于或等于另一数.按常规的思路,只需判断y-x的结果符号位为0还是1,然而在有限位机器中,当x,y异号时可能出现减法溢出情况:
1.y>0且x<0,y-x<0(正溢出)
2.y<0且x>0,y-x>0(负溢出)
因此,分以下情况类型考虑:
1.当x,y同号,不用考虑溢出,直接按常规思维处理;
2.当x,y异号,只需考虑二者各自的符号,拿x为例,x符号位是1说明一定有x<y,x符号位是0,说明一定有x>y.
实现代码:
int isLessOrEqual(int x, int y) {
int x_sign = x>>31&0x1;
int y_sign = y>>31&0x1;
int d = x_sign^y_sign;
int res = y+(~x+1);
int sign = (res>>31)&0x1;
return (!d&!sign)|(d&x_sign);
}
九.logicalNeg(x)
puzzle分析:
本题要求使用除’!‘之外的运算符来实现求某数的非的功能.根据非零数的负数符号位与该数不同,0与最小补码的负数都是自身的性质,可以根据数x与-x的符号位中是否出现1来判断它是不是非零数.因此用到’|'或运算符将x与-x进行"或运算",只要二者中有一个符号位为1,就说明一定是非零数,此时得到符号位为1的数.再将其算术右移得到全1(表示的值是-1),加1即得0.反之若输入是0,则最终得到全0,加1便得1.
实现代码:
int logicalNeg(int x) {
return ((x|(~x+1))>>31)+1;
}
十.howManyBits(x)
puzzle分析:
本题要求计算某个数按二进制补码表示所需的最小位数.可计算原数绝对值的编码表示中数值位最高位在第几位.再加上符号位,即为最终所求位数.(因为在Tmin+1~Tmax之间一一对应的数都需要相同位数来表示)
以下为思路:
1.sign = x>>31
可获得x的符号;x = (sign&~x)|(~sign&x)
是为了消除x的符号位,x为正值不变,x为负得到~x.
2.然后,将32位数字分为两个16位,将x右移16位,看高16位中是否有非零位,若有,则低16位一定是有效位,则总位数+16,再把x右移16位,以便下一步分析高16位的有效位数情况;若没有,则高16位都无效,低16位不全有效,下一步继续分析低十六位.
3.以此类推,逐步分成高低8位,4位,2位,1位,根据判断结果加入总位数中.
4.最终结果再加上符号位即得真正所求的总位数.
(另外,还需考虑0和-1这两种特殊情况,发现沿用上述思路同样成立~~)
实现代码:
int howManyBits(int x) {
int b16,b8,b4,b2,b1,b0;
int sign = x>>31;
x = (sign&~x)|(~sign&x);
b16 = (!!(x>>16))<<4;
x = x>>b16;
b8 = (!!(x>>8))<<3;
x = x>>b8;
b4 = (!!(x>>4))<<2;
x = x>>b4;
b2 = (!!(x>>2))<<1;
x = x>>b2;
b1 = (!!(x>>1));
x = x>>b1;
b0 = x;
return b16+b8+b4+b2+b1+b0+1;
}
十一. floatScale2(uf)
puzzle分析:
本题要求返回将输入的32位浮点表示数的值按浮点运算规则乘以2得到的32位数.
首先需要提取出32位无符号整型转化为浮点表示时各字段(符号,阶码,小数).要提取出阶码字段,可利用无符号数0 11111111 00000000000000000000000(B),即十六进制下的0x7F800000与输入的uf进行按位与运算,再右移23位即可得到阶码exp = (uf&0x7F800000)>>23
(是一个正数).符号字段通过sign = uf&(1<<31)
来获取.
根据阶码字段exp的值可分以下几种情况考虑:
1.当exp=0时,表示的值就取决于小数字段,若小数字段为0,则乘以2应返回本身;若非0,则可将小数字段左移一位 (由于除符号位外,其他字段全为0,这里通过右移uf,再并上符号位即可补充舍掉的符号位).
2.当exp=11111111B(即255)时,要么是无穷大,要么是NAN,因此返回自身即可;
3.当exp不为0或255时,要想乘以2只需将exp加一即可(但需要注意当exp为254时,加一则会溢出为无穷大,此时 应返回无穷大,即0x7f800000|sign
)
实现代码:
unsigned floatScale2(unsigned uf) {
int exp = (uf&0x7F800000)>>23;
int sign = uf&(1<<31);
if(exp == 0) return (uf<<1)|sign;
if(exp == 255) return uf;
if(++exp == 255) return 0x7F800000|sign;
return (exp<<23)|(uf&0x807FFFFF);
}
十二.floatFloat2Int(uf)
puzzle分析:
浮点数计算公式为: V = (-1)s * M * 2E (M = f+1,E = exp-bias,bias = 2k-1-1)
因此首先计算出s = uf>>31
,E = ((uf&07xf800000)>>23)-127
,f = (uf&0x007fffff)|0x00800000
(由于f默认隐含1,故将指数段最低位设为1以将其规范化)
下面进行分类:
1.当阶码字段和小数字段全为0(即+0或-0,都是0)时,转换整数就是0.
2.当指数E<0时,值小于1,转换整型后返回0;当E>31时,指数太大无法表示,返回0x80000000.
3.当0<=E<=31时,由于小数字段长23位,故应再分两类:
a)E<23,小数字段实际表示的数超过原数,故应对其截断,通过右移(23-E)位来实现;
b)E>=23,小数阶段实际表示的数小于原数,故应对其延展,通过左移(E-23)位来实现.
最后处理尾数部分,并检查是否发生了溢出:
a)如果尾数的符号与原始浮点数的符号相同,并且没有溢出(即尾数的最高位不是符号位),则返回尾数f;
b)如果尾数表示负数且发生了溢出,返回TMIN;
c)如果尾数表示正数且发生了溢出,返回TMAX(通过取反加1得到).
实现代码:
int floatFloat2Int(unsigned uf) {
int s = uf>>31;
int E = ((uf&0x7f800000)>>23)-127;
int f = (uf&0x007fffff)|0x00800000;
if(!(uf&0x7fffffff)) return 0;
if(E<0) return 0;
if(E>31) return 0x80000000;
if(E>23) f<<=(E-23);
else f>>=(23-E);
if(!((f>>31)^s)) return f;
else if(f>>31) return 0x80000000;
else return ~f+1;
}
十三.floatPower2(x)
puzzle分析:
本题要求计算2的整数幂.由于是2的整数次幂,因此不用考虑小数字段.又由于输入的是指数,可将其加上偏置bias=2^8-1^-1=127
来转换成阶码exp.由于规格化数最小只能表示2-126,即exp=1,因此下面针对exp的值进行分类讨论:
1.当exp<=0时,无符号数不能表示该数,此时返回0;
2.当exp>255时,结果太大,不能表示出来,此时返回正无穷;(正无穷+INF通过代码0xff<<23
来获得)
3.其他情况下,只需将exp左移23位即可得到2x.
实现代码:
unsigned floatPower2(int x) {
int INF = 0xff<<23;
int exp = x + 127;
if(exp <= 0) return 0;
if(exp >= 255) return INF;
return exp<<23;
}