写这个之前最好先把《深入理解计算机系统》的第二章学一下,或者有一点点计算机基础的也可以跟我一样硬写(但还是有一点痛苦的),然后写到最后三题的时候学一下IEEE754浮点数格式。
由于我用的是Mac,得搭一下环境,详看这篇文章(window的环境怎么搭也在里边,不搭环境但是初次接触datalab也可以看一下,会对datalab有更清晰的认识):
Introduction to CSAPP(八):Datalab - 知乎
写的时候注意符号优先级,不确定就还是按想要的顺序加上括号吧,还要注意规范化。
看不懂我的解析可以去看一下这篇:
CSAPP:DataLab详细解析_prician的博客-CSDN博客_datalab
这篇文章对我的帮助很大。
1.bitXor(x, y)
要求:仅使用 ~ 和 & 完成异或运算。
运算:~ &
做法:画个真值表
进行一个德摩根律的应用
按照德摩根律写出代码,即:
int bitXor(int x, int y) {
return (~((~x)&(~y)))&(~(x&y));
}
2. tmin()
要求:返回二进制补码表示的最小整数。
运算: ! ~ & ^ | + << >>
做法:返回1000000...(31个0)此为补码表示的最小整数,即只需将1左移31位即可
int tmin(void) {
return 1<<31;
}
3.isTmax(x)
要求:判断 x 是否为补码表示的最大整数,是则返回1。
运算:! ~ & ^ | +
做法:
在32位中,补码表示的最大整数为011111...,即该题目为判断x是否为0111111...,若是,则满足:(~x)^(x+1)=0(在纸上写出来看看),那么此时返回(!(~x^(x+1)))即可返回1;
但是当x为11111...时,也满足(~x)^(x+1)=0,那么我们需要排除这个情况,当x为1111...时,(~x)^0x0=0,将其结果规格化,得到(!!(x+1)^0x0),这一步是使x=11111...时返回0,使x为011111...时返回1。
将以上两个式子进行&运算即可。
int isTmax(int x) {
return (!(~x^(x+1))) & (!!(x+1)^0x0);
}
4.allOddBits(x)
要求:判断 x 二进制表示下的奇数位是否全为 1,是则返回1。
运算:! ~ & ^ | + << >> (规则允许使用最大为 0xFF 的整数
做法:
只需判断奇数位是否全为1即可。(注意二进制数是从第0位开始的)
一个随便的8位二进制数,若其奇数位全为1,&10101010后,得到10101010,得到的结果^10101010后为0,那么一个随便的32位二进制数同理,所以我们首先需要获得10101010...(32位)这个数。
10101010为0xAA,我们将其左移8位后再 | 0xAA,得到0xAAAA:10101010...(16位),
同理也可得到10101010...(32位),再按照上面的逻辑编写代码即可。
int allOddBits(int x)
{
int a = 0xAA;
int aa = a | (a << 8);
int aaa = aa | (aa << 16);
return !((x & aaa) ^ aaa);
}
5.negate(x)
要求:计算返回 -x。
运算:! ~ & ^ | + << >>
做法:-x=~x+1,直接返回就好了
int negate(int x) {
return ~x+1;
}
6.isAsciiDigit(x)
要求:判断值 x 是否在范围 [0x30, 0x39] 中。
运算: ! ~ & ^ | + << >>
做法:
若x在这个范围内,则 0x39+(-x)>=0,x+(-0x30)>=0;
由上题可知,-x=~x+1,即可得到下列式子,将式子的结果分别赋值给a,b,再获得他们各自的符号位即可判断x的值是否在范围内了。(正数符号位为0,负数符号位为1)
int isAsciiDigit(int x) {
int a = 0x39+(~x+1);
int b = x+(~0x30+1);
int c = !(a>>31) & !(b>>31);
return c;
}
7.conditional(x, y, z)
要求:执行 x ? y : z:当 x 不为 0 时,返回 y;否则返回 z。
运算:! ~ & ^ | + << >>
做法:
当x不为0时,!!x=1,将其变为其相反数-1(为111111...)后&y,即可返回y;
当x为0时,!!x=0,其相反数仍为0,则将其相反数取反后得到(11111....)&z后,即可返回z。
int conditional(int x, int y, int z) {
int a = !!x;
int b = ~a+1;
return (b&y) | (~b&z);
}
8.isLessOrEqual(x, y)
要求:判断 x <= y,x<=y时返回1。
运算:! ~ & ^ | + << >>
做法:
判断y-x>0,可以将y+(~x+1)的结果赋值给a,通过判断a的符号位,来判断y-x是否>0。
在x=y时也需返回1,此时我们返回!(x^y)即可。
但那么做具有局限性:
1.在a的符号位为0时,此时应该为y-x>0,返回1,
但当y<0,x>0时,算出:y-x>0,为错的,因此这种情况应返回0。
2.在a的符号位为1时,此时应该为y-x<0,返回0,
但在y>0,x<0时,算出:y-x<0,为错的,因此这种情况应返回1。
因此我们不只需要获取a的符号位,还要获取x,y的符号位进行判断。
int isLessOrEqual(int x, int y) {
int a = y + (~x+1);
int f1 = !(x^y);
int fa = a>>31;
int fx = x>>31;
int fy = y>>31;
return ((!fa)&((!fy)|fx)) | ((!fy)&fx&fa) | f1;
}
9.logicalNeg(x)
要求:计算 !x:当 x = 0 时返回 1;当 x ≠ 0 时返回 0。
运算:~ & ^ | + << >>
做法:当x不等于0时,x|(-x)的符号位为1,此时取反
int logicalNeg(int x) {
int a = (x | (~x+1))>>31;
return (~a)&1;
}
10.howManyBits(x)
要求:使用二进制补码表示 x 的最少位数。
运算:! ~ & ^ | + << >>
做法:
当x>=0时,位数取决于1的最高位数;当x<=0时,位数则取决于0的最高位数。那么我们先将负数取反,将问题统一成计算 1 的最高位,算术右移获得符号位后再与自身进行异或即可完成。
使用二分查找法去找1的最高位数,先将32位数分为16位,再分为8位,4位,2位,1位进行查找。
拿出一个细说
res为计数器,首先一定有符号位,所以计数器初始值为1。
在第一个二分中,我们先将x分为前16位和后16位,规格化前16位(!!(x>>16))可以知道前16位有没有1,如果有,则x的位数肯定包括后16位,此时!!(x>>16)=1,计数器加上1*16(左移4位即为乘以16)可以获得目前已知的位数;再将x右移16位,即可将前面的16位再继续二分;如果前16位没有1,则将后面的16位继续二分查找,直到得到x的最少位数。
bit = !!(x>>16)<<4;
res = res + bit;
x = x >> bit;
详细代码如下:
int howManyBits(int x) {
int bit;
int res = 1;
x = x ^ (x >> 31);
bit = !!(x>>16)<<4;
res = res + bit;
x = x >> bit;
bit = !!(x>>8)<<3;
res = res + bit;
x = x >> bit;
bit = !!(x>>8)<<3;
res = res + bit;
x = x >> bit;
bit = !!(x>>4)<<2;
res = res + bit;
x = x >> bit;
bit = !!(x>>2)<<1;
res = res + bit;
x = x >> bit;
bit = !!(x>>1);
res = res + bit;
x = x >> bit;
return x + res;
}
11.floatScale2(uf)
要求:计算 2 * uf,若 uf 为特殊值值时,直接返回 uf。
运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。
做法:
去找一下IEEE754浮点表示看一下,再回来会一目了然。
如果uf为特殊值,即e=0xff,直接返回uf;
如果uf为非规格化数,即e为0,乘上系数 2 时,阶码是否变动看 2f 是否大于等于 1,即 f 最高位是否为 1。由于阶码在尾数的高位,该情况下位数左移 1 位即可。
如果是规格化数,e+1后,将s,e,f分别进行移位运算再用|连在一起就好了。
详细代码如下:
unsigned floatScale2(unsigned uf) {
unsigned s = uf >> 31 & 0x1; //取得s
unsigned e = uf >> 23 & 0xff; //&上111111111,取得exp
unsigned f = uf ^ (s << 31) ^ (e << 23); //取得frac
//也可以 unsigned f = (uf & 0x7fffff)
if (!(e^0xff))
return uf;
if (!e)
return (s << 31) | (f << 1);//f左移1位相当于乘了2
return (s << 31) | ((e + 1) << 23) | f;//e+1也相当于乘了2
}
12.floatFloat2Int(uf)
要求:将浮点数 uf 转换成整数。
运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。
做法:
浮点数能表示的范围大于无符号整数的范围,我们首先还是取出s、exp、frac,
1.若为0,此时exp=0,frac=0,则返回0.
2.若为最值或NaN则返回1<<31
3.若为非规格化数,若数字很接近于0(此时exp为0,直接与第一个判断合并了),则返回0
4.若为规格化数,先将23位位数变为整数,再判断E为多少,大于31则溢出,小于0则返回0,再根据和23的差调整位数。
int floatFloat2Int(unsigned uf) {
unsigned s = uf >> 31 & 0x1;
unsigned e = uf >> 23 & 0xff;
unsigned f = uf ^ (s << 31) ^ (e << 23);
//表示为0或者非规格化
if (!(e | f)) //e=0或f=0时返回0
return 0;
//Int/NaN
if (e == 0xff)
return 1<<31;
int ee = e - 127;
f = f | (1 << 23);
if (ee > 31)
return 1 << 31;
else if (ee < 0)
return 0;
if (ee >= 23)
{
f <<= (ee - 23);
}
else
{
f >>= (23 - ee);
}
// if (e > 30 + (s & !f))
// return 0x80000000u;
// f = ((1 << 23) | f) >> (23 - e);
if (s)
return -f;
return f;
}
13.floatPower2(x)
要求:使用浮点数表示 2^x。无法表示时:过小返回 0,过大返回 +INF。
运算:Integer/unsigned 相关运算;||,&&,if 和 while 等判断语句。
做法:
详细代码如下所示:
unsigned floatPower2(int x) {
x = x + 0x7f;
if (x < 0) return 0;
return x < 0xff ? x << 23 : 0x7f800000u;
}
ok就到这,希望能够对你有所帮助。