csapp datalab
Posted N4tural
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csapp datalab相关的知识,希望对你有一定的参考价值。
异或运算
int bitXor(int x, int y)
return (~(~x&~y)&~(x&y));
题目要求用~和&表达异或,异或的定义算术表达式是(a&~b)|(~a&b)
,可以这样理解方便记忆:b原来为0的位取反变成1,这样和a为1的位进行按位异或就可以得到1,但是还有另一种情况就是a为0的位和b为1的位,然后将这两种情况取并集就得到了
然后推导另外两种,主要是德摩根定理的应用
(a&~b)|(~a&b) = (a|~a)&(a|b)&(~a|~b)&(b|~b)
= 1&(a|b)&(~a|~b)&1
=(a|b)&(~a|~b)
=~~(a|b)&~~(~a|~b)
=(~(~a&~b))&~(a&b)
32位最小值
int tmin(void)
return 1<<31;
这个直接左移,后面为0,就是书上说的-2^(w-1) +0
位最大值
int isTmax(int x)
return !(~(x+1)^x)&!!(x+1);
比如0111这类如果加1就是1000,和原来的位是相反的,所以直接异或,异或结果为0就是相同的,代表就是0111这类数,但是-1也满足条件,所以我们要与上一个!!(x+1),也就是取交集
奇数偶数位
int allOddBits(int x)
return !((x&0xAAAAAAAA)^0xAAAAAAAA);
判断是不是和A:1010类似的就行了
取负数
int negate(int x)
return ~x+1;
补码加1,没什么好说的
判断是否在范围内
int isAsciiDigit(int x)
int a = !(x>>4 ^ 0x3);
int b = x & 0xf;
int c = ~0xA + 1;
int d = !!((b+c) & 0x8000);
return a&d;
分思路的话就是分两步,第一是判断0x39和0x30的倒数第二个字节,所以右移4位后如果是3就把a置为1
然后取最低字节,并且加上-0xA,如果小于0,就在范围内,判断小于0就判断最高位是否为0就行了
三目运算符
int conditional(int x, int y, int z)
int a = (~!!x)+1;
return (a&y)| (~a&z);
x为0,返回z,x非0返回y
所以让x非两次,如果非零的话a就是32个1,是0的话两次非+1溢出后就是32个0
所以用一个特定变量flag来控制返回y还是返回z就用这个固定的套路(flag & x)|(~flag & y),让flag因为两种情况,所以取并集
比较大小
int isLessOrEqual(int x, int y)
int flag = !(x >> 31) ^ !(y >> 31);
int a = flag & (x>>31);
int b = !flag & !((y+(~x+1))>>31);
return a | b;
如果x > y 返回0,若x <=y 返回1
首先判断x,y是否异号,如果异号,只要x的符号位为1,那就肯定小,就直接返回1就可以了
还有一种情况就是同号,那就还是让y加上x的补码,判断符号位,flag用来判断是否同号,两者取交集。
逻辑非
int logicalNeg(int x)
return ((x|(~x+1))>>31)+1;
int logicalNeg(int x)
return !(~(!!x)+1);
(~(!!x)+1)
两种方法
第二种写法还是非0与0的问题,也可以用和上面三目运算符一样的方法,这里说一下第一段如果是0的话,0和自己本身补码的符号位是相同的,都是0,所以让他置为1,其他的加1后由于是算术右移,溢出后变0
多少位
int howManyBits(int x)
int flag = x >> 31;
x = (flag & ~x)|(~flag&x);//正数不变,负数取反
int b16 = !!(x>>16)<<4;//判断第十六位是不是0,如果不是,说明高16位上存在最左边的1,并且为后面移动16位作准备
x>>=b16;/*根据16位的情况判断是否移动*/
int b8 = !!(x>>8)<<3;
x>>=b8;
int b4 = !!(x>>4)<<2;
x>>=b4;
int b2 = !!(x>>2)<<1;
x>>=b2;
int b1 = !!(x>>1);
x>>=b1;
int b0 = x;
return b16+b8+b4+b2+b1+b0+1;//位长是最左边1再加上1
这就是二分法查找,找位数就是找最左边的1,但是负数要取反,就用符号位flag来控制,上面说过的那个模板,如果16位是1,那就要往它高位再找看看有没有1,如果不是1,那就不移动,往8位找,下面都是一样的
浮点数乘2
unsigned floatScale2(unsigned uf)
unsigned frac = uf & 0x007fffff;
unsigned exp = (uf >> 23) & 0xff;
unsigned s = uf >> 31;
if(!exp)
frac <<= 1;
if(frac >> 23)
frac = frac & 0x007fffff;
exp++;
else if(exp!=0xff)
exp++;
return (s<<31)|(exp<<23)|frac;
这里分为几种情况
第一种 规格化数
exp不为0
第二种 非规格化数
exp为0
第三种 特殊值
exp全1,frac不为0,为NAN,f为0则是无穷大
那f左移就是乘2,如果溢出那就exp加1就行了
浮点数转换
int floatFloat2Int(unsigned uf)
int frac = (uf & 0x007fffff) | 0x00800000;
int exp = ((uf>>23) & 0xff) - 127;
int s = uf>>31;
int tar = 0;
if (s)
s = -1;
else
s = 1;
if (exp > 31)
return 0x80000000;
if (exp < 0)
return 0;
if (exp > 23)
tar = s * (frac << (exp - 23));
else if (exp < 23)
tar = s * (frac >> (23 - exp));
return tar;
还是分为三种情况,e为255即返回0x80000000,这里就是exp = 255-bias,因为int最大表示范围是2^31
,这里因为M为1~2-ipx,所以E小于2^31
次方
如果e小于0返回0
如果是规格化数,有公式 V = (-1)^s*M*2^E
M = 1 + frac
所以第一步就把24位置为1了,补上了1
如果小于23,就要把23-E位舍去,因为最小规格数是2^-126,frac有23位,所以如果小于23的,通过f和exp之间的差值相互相乘抵消为1。
如果大于23,再乘抵消后剩余的就行了
边界判断
最小非规格数:exp = 0 ,frac = 00……01, E = 1-bias 2^-23*2^-126
最大非规格数:exp = 0, frac = 11……11, E = 1-bias (1-i)*2^-126
最小规格数:exp = 0……1,frac = 00……00 ,E = e - bias ,M = 1,1*2^(-127+1)
最大规格数:exp = 1……10,frac = 11……11 ,E = e - bias ,M = 1+frac,(2-i)*2^(-127+(2^8-1-1))
unsigned floatPower2(int x)
int exp = x+127;
if(exp >= 0xff)
return 0x7f800000;
if(exp <= 0)
return 0;
return exp << 23;
通过上面的实验可以得到,如果把值右移的话过小,可以用非规范化来表示
传入的x是E值,直接加上bias如果过大,直接return题目要求的值
如果小于或等于0,那就是非规格数,返回0,其他值就是规格码左移23位,让它成为2^E返回就行了
以上是关于csapp datalab的主要内容,如果未能解决你的问题,请参考以下文章