data lab

266 阅读5分钟

data lab

任务目标

熟悉整数和浮点数的位级表示,解谜题

使用限定的位运算、布尔运算和算术运算来完成

限定的操作符数量

每个函数具有特定的分值

整数题目构造的常数仅在范围 0x0 ~ 0xff

操作指南

LAB 官网

一共 13 个需要补充的函数,所有的工作都只需修改 bits.c文件

使用wget下载单个文件  wget <http://www.linuxde.net/testfile.zip>
解压   tar -xf datalab-handout.tar
检测 ./btest
名称描述操作符难度指令数目举例
bitXor(x,y)实现 x^y~ &114bitXor(4, 5) = 1
tmin()返回最小补码! ~ & ^+ << >>14
isTmax(x)判断是否是补码最大值! ~ & ^+110
allOddBits(x)判断奇数位是否都是1! ~ & ^+ << >>212
negate(x)实现 -x! ~ & ^+ << >>25
isAsciiDigit(x)判断是否0x30 <= x <= 0x39! ~ & ^+ << >>315
isAsciiDigit(0x3a) = 0.
isAsciiDigit(0x05) = 0.
conditional(x,y,z)x ? y : z! ~ & ^+ << >>316
conditional(0,4,5) = 5
isLessOrEqual(x,y)判断 x ≤ y! ~ & ^+ << >>324
logicalNeg(x)!x~ & ^+ << >>412
logicalNeg(0) = 1
howManyBits(x)表达 x 的最少位数! ~ & ^+ << >>490
howManyBits(0) = 1
howManyBits(-1) = 1
howManyBits(0x80000000) = 32
floatScale2(f)求2乘一个浮点数Any integer/unsigned operations incl., &&. also if, while4
floatFloat2Int(f)将浮点数转换为整数430
floatPower22的X次方430

前置知识

image.png

  1. 位级运算 ~ | & ^
  2. 逻辑运算符 ||、&&、!(分别对应OR AND NOT),只会返回1和0.

image.png

  1. 移位运算
  2. 补码

image.png

image.png 补码:最大值 0 1111 1111 …

image.png

  最小值 1 0000 0000 
  1. 浮点数

image.png

𝑣=(−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 加密解密算法 - 奇偶位交换


### 参考

[CSAPPDataLab详解,没有比这更详细的了](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>