data lab
任务目标
熟悉整数和浮点数的位级表示,解谜题
使用限定的位运算、布尔运算和算术运算来完成
限定的操作符数量
每个函数具有特定的分值
整数题目构造的常数仅在范围 0x0 ~ 0xff
操作指南
一共 13 个需要补充的函数,所有的工作都只需修改 bits.c文件
使用wget下载单个文件 wget <http://www.linuxde.net/testfile.zip>
解压 tar -xf datalab-handout.tar
检测 ./btest
| 名称 | 描述 | 操作符 | 难度 | 指令数目 | 举例 |
|---|---|---|---|---|---|
| bitXor(x,y) | 实现 x^y | ~ & | 1 | 14 | bitXor(4, 5) = 1 |
| tmin() | 返回最小补码 | ! ~ & ^ | + << >> | 1 | 4 |
| isTmax(x) | 判断是否是补码最大值 | ! ~ & ^ | + | 1 | 10 |
| allOddBits(x) | 判断奇数位是否都是1 | ! ~ & ^ | + << >> | 2 | 12 |
| negate(x) | 实现 -x | ! ~ & ^ | + << >> | 2 | 5 |
| isAsciiDigit(x) | 判断是否0x30 <= x <= 0x39 | ! ~ & ^ | + << >> | 3 | 15 |
| isAsciiDigit(0x3a) = 0. | |||||
| isAsciiDigit(0x05) = 0. | |||||
| conditional(x,y,z) | x ? y : z | ! ~ & ^ | + << >> | 3 | 16 |
| conditional(0,4,5) = 5 | |||||
| isLessOrEqual(x,y) | 判断 x ≤ y | ! ~ & ^ | + << >> | 3 | 24 |
| logicalNeg(x) | !x | ~ & ^ | + << >> | 4 | 12 |
| logicalNeg(0) = 1 | |||||
| howManyBits(x) | 表达 x 的最少位数 | ! ~ & ^ | + << >> | 4 | 90 |
| howManyBits(0) = 1 | |||||
| howManyBits(-1) = 1 | |||||
| howManyBits(0x80000000) = 32 | |||||
| floatScale2(f) | 求2乘一个浮点数 | Any integer/unsigned operations incl. | , &&. also if, while | 4 | |
| floatFloat2Int(f) | 将浮点数转换为整数 | 4 | 30 | ||
| floatPower2 | 2的X次方 | 4 | 30 | ||
前置知识
- 位级运算 ~ | & ^
- 逻辑运算符 ||、&&、!(分别对应OR AND NOT),只会返回1和0.
- 移位运算
- 补码
补码:最大值 0 1111 1111 …
最小值 1 0000 0000 …
-
浮点数
𝑣=(−1)^𝑠 *𝑀* * 2^𝐸
题目解法
1. [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) {
return ~(x&y) & ~(~x & ~y);
}
2. [1]补码的最小 int 值
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1 << 31;
}
// 思路: 最小补码 1000 0000 0...
// 最大补码 0111 1111 ...
// todo: 补码最大 int 值是
3. [1] 通过位运算判断是否是补码最大值 0111 1111 ... 若是,返回 1;否则返回 0
/*
* 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 + 1;
int z = y ^ x;
return !(~y) & !!z;
}
// 思路:观察给到的运算符,有 +
// 返回值为0或1,因此想办法凑出0,然后使用”!”获得结果
// 无符号数加法,如果两个 w 位的数字相加,结果是 w+1 位的话,
// 就会**丢弃掉最高位**,实际上是做了一个 mod 操作
// 补码加法溢出:正溢出011+010(3+2) = 101(-3)和负溢出100+101(-4+(-3)) = 001(1)
//todo: 判断是否是最小值 1000 0000 ...
4. [1] 判断奇数位(1010)是否都是1,若是返回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
*/
// 思路:构造奇数位是1的 mask 进行与运算 (构造范围是0x00 - 0xff)位掩码
// 当作公式记住
int allOddBits(int x) {
int mask = 0xAA + (0xAA << 8);
mask = mask + (mask << 16);
return !((mask & x) ^ mask);
}
// todo:公式扩展
5.[1] ~x + x = 0xffffffff, -x = ~x + 1 补 码=反码+1
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x+1;
}
6. [0]
/*
* 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) {
// make sure 0x3x
int isPrefixBe3 = !((x >> 4) ^ 0x3);
// make sure 0=< y <= 9
int y = x & 0xf; // 保留后四位
int res = !(y ^ 0x8) | !(y ^ 0x9) | !(y & 0x8); //三类情况
return isPrefixBe3 & res;
}
7. [0] 三元表达式
/*
* conditional - same as x ? y : z
* Example: conditional(2,4,5) = 4
* Example: conditional(0,4,5) = 5
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 16
* Rating: 3
*/
// 思路:关键在于如何表示判断 x 是不是等于0
int conditional(int x, int y, int z) {
x = ~(!!x) + 1;
return (x & y) | (~x & z); //0的补码是0;1的补码是-1
}
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) {
}
9. [0] 不用 ! 实现逻辑非运算
/*
* 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
*/
// 思路: logicalNeg(3) = 0, logicalNeg(0) = 1
0和最小数的补码是本身,0xffffffff+1
int logicalNeg(int x) {
return ((x|(~x+1))>>31)+1;
}
10. [0]
/* howManyBits - return the minimum number of bits required to represent x in
* two's complement
* Examples: howManyBits(12) = 5 01100
* howManyBits(298) = 10
* howManyBits(-5) = 4 1011
* howManyBits(0) = 1 0
* howManyBits(-1) = 1 1
* howManyBits(0x80000000) = 32
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
// 如果是一个正数,则需要找到它最高的一位(假设是n)是1的,再加上符号位,结果为n+1
// 如果是一个负数,则需要知道其最高的一位是0的
int howManyBits(int x) {
int sign=x>>31;
x = (sign&~x)|(~sign&x); //如果x为正则不变,否则按位取反
// 不断缩小范围, 二分法更高效
int b16 = !!(x>>16)<<4;//高十六位是否有1, 1<<4 = 10000 = 16
x = x>>b16;//如果有(至少需要16位),则将原数右移16位
int b8 = !!(x>>8)<<3;//剩余位高8位是否有1
x = x>>b8;//如果有(至少需要16+8=24位),则右移8位
int b4 = !!(x>>4)<<2;//同理
x = x>>b4;
int b2 = !!(x>>2)<<1;
x = x>>b2;
int b1 = !!(x>>1);
x = x>>b1;
int b0 = x;
return b16+b8+b4+b2+b1+b0+1;//+1表示加上符号位
}
11. [0] 实现 <=
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
// 符号不同正数大,符号相同看差值y符号, 考虑溢出
int isLessOrEqual(int x, int y) {
int mask = 1 << 31;
int xSign = x & mask;
int ySign = y & mask;
// same sign => 0, else 0xffffffff
int signMask = !(xSign ^ ySign) + (~0);
int z = y + ~x +1;
// if x,y have the same sign, then z > 0 else ySign > 0
int res = (!( z & mask) & (~signMask)) + (!ySign + signMask);
return res;
}
// 浮点数
1.[0]
求2乘一个浮点数
/*
* 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
*/
// 首先排除无穷小、0、无穷大和非数值NaN,
// 此时浮点数指数部分(真正指数+bias)分别存储的的为0,0,,255,255。
// 无穷小和0只需要将原数乘二再加上符号位就行了。
// 剩下的情况,如果指数+1之后为指数为255则返回原符号无穷大,否则返回指数+1之后的原符号数。
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;
exp++;
if(exp==255) return 0x7f800000|sign;
return (exp<<23)|(uf&0x807fffff);
// (sign << 31) | (exp << 23) | frac;
// frac=uf&0x7FFFFF;
// sign=uf>>31&0x1;
// exp = (uf&0x7f800000)>>23;
}
2.[0]将浮点数转换为整数
/*
* 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
*/
先计算出E=exp-bias
1. 如果是小数 E< 0的情况我们直接返回0
2. 如果是exp=255 的情况直接返回0x80000000u 这里注意如果超范围了也会直接返回0x80000000u
因此可以直接用E>=31 来判断
3. 如果是规格化数则我们进行正常处理
a.先给尾数补充上省略的1, frac = 1.xxxxx
b.判断E<23 则尾数需要舍去23-E位
// frac 是23位 -〉
// 1.11*2^1 -> 11.1*2 舍去2-1位,小数补0,右移再左移
c.根据符号位返回就好
int floatFloat2Int(unsigned uf) {
unsigned exp = (uf&0x7f800000)>>23;
int sign=uf>>31&0x1;
unsigned frac=uf&0x7FFFFF;
int E=exp-127;
if(E<0)return 0;
else if(E >= 31){
return 0x80000000u;
}
else{
frac=frac|1<<23;
if(E<23) {//需要舍入
frac>>=(23-E);
}else{
frac <<= (E - 23);
}
}
if (sign)
return -frac;
else
return frac;
}
3. [0] 2的X次方
/*
* 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
*/
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;
}
// 2.0^x = 1.0*2^x 套公式
// 实际的exp = x + 127,检查是否越界即可,随后移位至exp的位置
总结
!((mask & x) ^ mask)
~x + x = 0xffffffff, -x = ~x + 1 补 码=反码+1
x & 0xf; // 保留后四位
!(y ^ 0x9) // 是9则返回1
!(y & 0x8) // 是0~7则返回1
扩展
todo 加密解密算法 - 奇偶位交换
### 参考
[CSAPP 之 DataLab详解,没有比这更详细的了](https://zhuanlan.zhihu.com/p/59534845)
[【读厚 CSAPP】I Data Lab](https://wdxtub.com/csapp/thick-csapp-lab-1/2016/04/16/)
[【读薄 CSAPP】壹 数据表示](https://wdxtub.com/csapp/thin-csapp-1/2016/04/16/)
[CSAPP:Lab1 -DataLab 超详解](https://zhuanlan.zhihu.com/p/339047608)
[](https://zhuanlan.zhihu.com/p/339047608)<https://zhuanlan.zhihu.com/p/339047608>