CSAPP_DataLab
Published:
注意可以使用的 op 以及 op 的总个数限制
代码参考:https://github.com/ChenYuHengSJTU/CSAPP_Labs/tree/main/datalab/datalab-handout
1
- 仅使用~和 & 来实现按位异或
- 想法来源:离散数学真值表
/*
* bitXor - x^y using only ~ and &
* Example: bitXor(4, 5) = 1
* Legal ops: ~ &
* Max ops: 14
* Rating: 1
*/
int bitXor(int x, int y) {
int m = (~x) & y;
int n = (~y) & x;
return ~((~m) & (~n));
}
2
- 返回Tmin
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
// easy
return (1 << 31);
}
3
- 这题着实把我整崩溃了
- 其实想法并不难,从 Tmax 的特性入手即可
- 0x7fffffff 左移 1 位(即 x*2 = x + x )后加 2 就是 0,当然需要排除 - 1(0xffffffff)的情况,即!!(x+1) 即可
- 但是在处理的过程中,发现给我的答错了
- 百思不得其解
- 后来使用 gdb 考察汇编,发现整个 isTmax 函数只有三行汇编,而且汇编的返回值一定是 0
- 解决方法:把 makefile 中的 - Og 的优化选项去掉,这样编译器才会乖乖算出 (x + x +2) 的值,否则就会优化掉
/*
* isTmax - returns 1 if x is the maximum, two's complement number,
* and 0 otherwise
* Legal ops: ! ~ & ^ | +
* Max ops: 10
* Rating: 1
*/
int isTmax(int x) {
int y = x + x + 2;
return ((!y) & (!!(x + 1)));
}
4
不断配合使用 « 和 ,来构造出 0xaaaaaaaa,x & 0xaaaaaaaa 后,如果确为 allOddbits,应该等于 0xaaaaaaaa,在加上 -0xaaaaaaaa (~0xaaaaaaaa+1),判断是否为 0
/*
* allOddBits - return 1 if all odd-numbered bits in word set to 1
* where bits are numbered from 0 (least significant) to 31 (most significant)
* Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 12
* Rating: 2
*/
int allOddBits(int x) {
// even-numbered bits can also be 1
int mask1 = 0xaa;
int mask2 = mask1 << 8;
int mask3 = mask2 << 8;
int mask4 = mask3 << 8;
int mask = (mask1 | mask2 | mask3 | mask4);
// x & mask clears the impact of even-numbered 1s
// to judge whether it equals to 0xaaaaaaaa
return !((x & mask) + (~mask) + 1);
}
5
- 在树状数组中习得
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
// easy
return (~x) + 1;
}
6
- 第一种方法超出的最多运算符数限制
- 第二种方法是取出最低位,然后先判断 0x30 中的 3,再分两种情况讨论最低四位,详见注释
//3
/*
* isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters '0' to '9')
* Example: isAsciiDigit(0x35) = 1.
* isAsciiDigit(0x3a) = 0.
* isAsciiDigit(0x05) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 15
* Rating: 3
*/
int isAsciiDigit(int x) {
// my first method run out of all the 15 available ops
// int mask1 = 0xff;
// int mask2 = mask1 << 8;
// int mask3 = mask2 << 8;
// int mask4 = mask3 << 8;
// int mask = (0xc0 | mask2 | mask3 | mask4);
// int c = (x & mask);
// int xx = x & 0x3f;
// int c1 = !(((xx + (~0x30) +1) >> 31) & (0x1)); // == 0
// int c2 = !((((~xx) + 1 + 0x39) >> 31) & (0x1)); // == 0
// return (!c) & (c1 & c2);
// first step is to fetch the low four bits
int mask = 0xf;
int low = mask & x;
// then judge the upper 28 bits;use ^ to judge whether it equals to 0x30
int xx = x + (~low) +1;
int c = xx ^ 0x30;
// two cases of the low four bits
// 0xxx or 1000 or 1001
int mid = low & 0x6;
int high = low >> 3;
return (!c) & ((!high) | (!mid));
}
7
- 需要一个小 trick:!!x 将 x 转换为 0 或 1
- 还有就是,得到 0 或 1 后,怎么转换为 0xffffffff,因为我们要用 0xffffffff & y 或 0xffffffff & z 来得到 y 和 z。
- 还是利用 0,1,-1 之间的位级关系
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
int conditional(int x, int y, int z) {
// use ! to judge , to change odinary nums to 0 or 1
// after we get 0 or 1
// for 0 : we can use !x to change it to 1 , then ~x + 1 to get -1 , with
// all bits of 1
// for 1 : we can use !!x to change it back to 1, then ~x+1 to get -1
return ((~(!(!x)) + 1) & y) | ((~(!x) + 1) & z);
}
8
- 判等:^
- 基本想法就是作差判断符号
- 但是需要考虑溢出,仅需考虑正数减负数的溢出
- 使用符号位判断溢出的特殊情况
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
// use ^ to judge equalance
// to avoid overflow , use the most significant bit to deal with different cases
int xs = x >> 31;
int ys = y >> 31;
return (!(x ^ y)) | (xs & (!ys)) | ((!(xs ^ ys)) & (x + (~y) +1) >> 31);
}
9
- 实现逻辑非
- 把数值分为三类:positive,negative,zero
- 取 x 与 - x 的符号位,在进行 & 0x1(因为对负数实行逻辑右移)+ 1,得到 xs 与 negxs
- 对于非零值,两者一定有一者为 0
- 对于零,两者均为 1
/*
* logicalNeg - implement the ! operator, using all of
* the legal operators except !
* Examples: logicalNeg(3) = 0, logicalNeg(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int logicalNeg(int x) {
// we divide all x into three groups: 0,positive,negative
// for negative and positive,one and only one of the significant bit of x and -x will be 1
// but for 0,they are all 0
// so we add 1 ,keep the trait for positive and negative numbers,but change the case of 0
// last,for non-zero : xs == 0 && negxs == 1 || xs == 1 && negxs == 0
// for zero : xs == 1 || negxs == 1
int xs = (((x >> 31) & 0x1) + 1) & 0x1 ;
int negxs = (((((~x) + 1) >> 31) & 0x1) + 1) & 0x1 ;
return xs & negxs;
}
10
- kind of hard
/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5
* howManyBits(298) = 10
* howManyBits(-5) = 4
* howManyBits(0) = 1
* howManyBits(-1) = 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int howManyBits(int x) {
int isZero = !x;
int mask = ((!!x) << 31) >> 31;
int flag = x >> 31;
x = ((~flag) & x) | (flag & (~x));
int bit_16, bit_8, bit_4, bit_2, bit_1, bit_0;
bit_16 = (!((!!(x >> 16)) ^ 0x1)) << 4;
x >>= bit_16;
bit_8 = (!((!!(x >> 8)) ^ 0x1)) << 3;
x >>= bit_8;
bit_4 = (!((!!(x >> 4)) ^ 0x1)) << 2;
x >>= bit_4;
bit_2 = (!((!!(x >> 2)) ^ 0x1)) << 1;
x >>= bit_2;
bit_1 = (!((!!(x >> 1)) ^ 0x1));
x >>= bit_1;
bit_0 = x;
int res = bit_16 + bit_8 + bit_4 + bit_2 + bit_1 + bit_0 + 1;
return (isZero | (mask & res));
}
11
- 浮点数的三道题均不难,更多的是考察对于 IEEE754 浮点数标准的理解记忆
- 第一题,分情况(规格化,非规格化,NAN)讨论即可
/*
* floatScale2 - Return bit-level equivalent of expression 2*f for
* floating point argument f.
* Both the argument and result are passed as unsigned int's, but
* they are to be interpreted as the bit-level representation of
* single-precision floating point values.
* When argument is NaN, return argument
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
unsigned floatScale2(unsigned uf) {
int expmask = 0xff << 23;
int s = uf >> 31;
int fmask1 = 0xff;
int fmask2 = fmask1 << 8;
int fmask3 = fmask2 << 7;
int fmask = fmask1 | fmask2 | fmask3;
int frac = uf & fmask;
int exp = (uf & expmask) >> 23;
if(exp && (exp - 0xff))
exp = exp + 1;
if(!exp)
frac = frac << 1;
uf = (s << 31) | (exp << 23) | (frac);
return uf;
}
12
- 也是只需分情况讨论即可,注意 exp 位域的最大值为 1111110,不能是 1111111
- 从这道题中可以 get 到,对于溢出,均处理为 inf,而不是 NAN
/*
* floatFloat2Int - Return bit-level equivalent of expression (int) f
* for floating point argument f.
* Argument is passed as unsigned int, but
* it is to be interpreted as the bit-level representation of a
* single-precision floating point value.
* Anything out of range (including NaN and infinity) should return
* 0x80000000u.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 30
* Rating: 4
*/
int floatFloat2Int(unsigned uf) {
int expmask = 0xff << 23;
int exp = (uf & expmask) >> 23;
int s = uf >> 31;
int fmask1 = 0xff;
int fmask2 = fmask1 << 8;
int fmask3 = fmask2 << 7;
int fmask = fmask1 | fmask2 | fmask3;
int frac = uf & fmask;
int M;
int E;
int bias = 127;
if(exp == 0xff){
return 0x80000000u;
}
if(exp && (exp - 0xff)){
M = 1 << 23 | frac;
E = exp - bias;
}
else{
M = frac;
E = 1 - bias;
}
int res;
if(E > 23){
res = M << E;
// do not forget this anything out of range
if(E >= 8) return 0x80000000u;
}
else if(E == 23) res = M;
else{
int e = 23 - E;
if(e >= 24) return 0;
res = M >> (23 - E);
}
if(s) return -res;
return res;
}
13
- 特别注意 float 的表示范围,是不对称的,既要考虑规格化,也要考虑非规格化
- 具体细节可以看书
/*
* floatPower2 - Return bit-level equivalent of the expression 2.0^x
* (2.0 raised to the power x) for any 32-bit integer x.
*
* The unsigned value that is returned should have the identical bit
* representation as the single-precision floating-point number 2.0^x.
* If the result is too small to be represented as a denorm, return
* 0. If too large, return +INF.
*
* Legal ops: Any integer/unsigned operations incl. ||, &&. Also if, while
* Max ops: 30
* Rating: 4
*/
#include<stdio.h>
unsigned floatPower2(int x) {
int exp;
int frac;
int s = 0;
int res = 0;
if(x >= 128){
exp = 0xff;
frac = 0;
s = 0;
}
else if(x < -126 - 23){
frac = 0;
exp = 0;
}
else if(x >= -126 - 23 && x <= -127){
exp = 0;
frac = 1 << (x - (-126 - 23));
}
else {
frac = 0;
exp = 127 + x;
}
res = (exp << 23) | (frac) | (s << 31);
return res;
}
Evaluation
./run.sh