• CSAPP实验记录(1)--------- DataLab


    Datalab

    Datalab实验是关于数据的机器级表示,实验要求实现给定的位级运算符,同时要满足一些要求,如只能使用某些限定的运算符,运算符总数不超过某数字等。第一次刷感觉难度还是很大的。

    题目一:bitXor

    //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) );
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    第一题,要求利用 ~ 和 & 实现按位异或。前面几道题还是比较简单的。
    思路:比较被熟知的异或表达式是这样的 : X o r ( x , y ) = ( ! x & y ) ∣ ( x & ! y ) Xor(x, y) = (!x \& y) | (x \& !y) Xor(x,y)=(!x&y)(x&!y)
    但是题目要求不能用按位与,所以考虑对这个式子取反:将或逻辑变成与逻辑
    ! X o r ( x , y ) = ! ( ! x & y ) & ! ( x & ! y ) !Xor(x, y) = !(!x \& y) \& !(x \& !y) !Xor(x,y)=!(!x&y)&!(x&!y)
    将这个式子再取反一次就可以得到Xor了

    int bitXor(int x, int y) {
       return ~( ~(~x & y) & ~(x & ~y) );
    }
    
    • 1
    • 2
    • 3

    题目二:tmin

    /* 
    * tmin - return minimum two's complement integer 
    *   Legal ops: ! ~ & ^ | + << >>
    *   Max ops: 4
    *   Rating: 1
    */
    int tmin(void) {
       return 1<<31;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    补码int的最小值,很简单。

    题目三:isTmax

    /*
    * 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) {
       //return 2;
       return !(~(x + x + 0x1)) & !(!(x + 0x1));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果 x 是补码int能表示的最大值,返回1,否则返回0.
    思路:int 补码能表示的最大值是 0 x 7 f f f f f f f 0x7fffffff 0x7fffffff,可以利用其特征使它向零转换。这个数字左移一位再加一之后是全一的位模式( 0 x f f f f f f f f 0xffffffff 0xffffffff),然后按位取反判断是否为0即可。还要注意 x = 0 x f f f f f f f f x = 0xffffffff x=0xffffffff,也满足同样的特征,所以要排除这种情况。

    题目四:allOddBits

    /* 
    * 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) {
       //return 2;
       int mask = 0xaa;
       mask = (mask << 8) + mask;
       mask = (mask << 16) + mask;
       return !((x & mask) ^ mask);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    要求:判断一个整数是否满足所有二进制权值为奇数的位都是1。
    思路:要考察某些特定的位,容易想到可以构造这些位的掩码,来吸取 x 的这些位。将吸取结果与 0 x a a a a a a a a 0xaaaaaaaa 0xaaaaaaaa异或,判断结果是否为0即可。
    还要注意本实验要求代码中不能出现大于 0 x f f 0xff 0xff 的立即数,所以需要用 0 x a a 0xaa 0xaa 不断左移来构造掩码。

    题目五:negate

    /* 
    * negate - return -x 
    *   Example: negate(1) = -1.
    *   Legal ops: ! ~ & ^ | + << >>
    *   Max ops: 5
    *   Rating: 2
    */
    int negate(int x) {
       //return 2;
       return (~x)+1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    给定 x ,返回相反数。计算机中整数取相反数就是求补码,这道题属于常识。

    题目六:isAsciiDigit

    //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) {
       int t1 = !(((~(0x07) & x) >> 3) ^ 0x06);
       int t2 = !(((~(0x01) & x) >> 1) ^ 0x1c);
       return t1 | t2;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    要求:判断x是否是ASCII字符 ‘0’~‘9’,即是否 0 x 30 < = x < = 0 x 39 0x30 <= x <= 0x39 0x30<=x<=0x39
    这道题还是有必要仔细记录一下的,因为后面还有一道题叫“isLessOrEqual” ,要求用位运算实现判断 x <= y,起初我想用一个思路解决这两道题,即将 x < = y x<=y x<=y转化成 x − y < = 0 x-y<=0 xy<=0再进行补码运算后判断正负号。没想到这个方法用在这两道题上都出了问题。
    这道题正确的思路:
    将0x30与0x39的二进制形式写出来

    0x30 = 0011 0000
    0x39 = 0011 1001
    
    • 1
    • 2

    可以看出来满足 0 x 30 < = x < = 0 x 39 0x30<=x<=0x39 0x30<=x<=0x39的x可以划分成两种情况:

    0011 0xxx           //x代表任意二进制数字
    0011 100x           //总共 2^3 + 2 = 10个数字,正好0x30~0x39也是十个数字
    
    • 1
    • 2

    然后就可以分两种情况分别判断了,(代码中的t1和t2)。

    题目七:conditional

    /* 
    * 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) {
       //return 2;
       int xx = ((!x) << 31) >> 31;
       return (~xx & y) | (xx & z);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    题目要求用位运算实现三目运算符 ‘ ? ? ?’,根据三目运算符的逻辑(x ? y : z)来看,可以知道它是类似于做一个二路选择,即当x为真时,选择(信号)y作为输出,否则选择(信号)z作为输出,因此本题可以参考数字逻辑中的二路选择器。将x变成全0或全1的掩码,吸取y和z的所有位。
    情况一:当x = 0时,掩码应该是全1的位模式。
    情况二:当x != 0时,掩码应该是全0的位模式。
    代码中的xx,即用x构造的掩码。

    题目八:isLessOrEqual

    /* 
    * 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) {
       int a = (x >> 31) & 0x1;             //x的符号
       int b = (y >> 31) & 0x1;             //y的符号
       int c1 = (a & ~b);                   // x<0且y>0的情况
       int c2 = (~a & b);                   // x>0且y<0的情况
       int e = y + (~x + 0x1);              //x-y
       int flag = e >> 31;
       return c1 | (!c2 & !flag);           //如果flag 和 c2 不同则说明了溢出了
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    判断两个数字x,y是否满足 x<=y,满足则输出1.
    起初我想将 x < = y x<=y x<=y转化成 x − y < = 0 x-y<=0 xy<=0再进行补码运算后判断正负号。但是评测时没有得分,查看数据之后才发现我没有意识到x-y存在溢出问题,如果x-y超过int表示范围,判断结果就会出错。所以这道题需要另外的思路。
    如果x和y的符号不同,那么正数一定是大于负数的。符号相同则做减法不会溢出,此时可以看差值的符号,详细见代码注释。

    题目九:logicalNeg

    /* 
    * 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) {
       return ((x | (~x + 0x1)) >> 31) + 0x1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    题目:不使用 ’ ! ‘,实现逻辑非,即x = 0输出1,x为非0,输出0.
    思路:容易想到如果位模式为全0,那么将其加上0x1后就得到true的逻辑,而全1的位模式,加上0x1后就变成全0,也就是false的逻辑。
    那么我们可以定义一组操作,使得当x = 0时,经过操作后结果仍为0,之后加上0x1,就变成了true;当x != 0时,经过这组操作变成全1的位模式,我们要找到这样的操作。
    对于补码运算,我们知道除了0(其实还有1000…0,但是这种情况没有例外)的补码是自己本身,其他数字的补码都是其相反数,符号位改变。那么如果用原数x与(~x + 0x1)进行按位或,就可以得到符号位为1,对其进行算数右移即可得到全1的位模式,而0的补码是其本身,经过这套操作,结果还是全0的位模式。
    (10000.0的补码也是其本身,但因为其本身符号位就是1,所以 x | (~x + 0x1))后也是全1的位模式,所以这种情况不是例外)。

    题目十:howManyBits

    /* 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 b16,b8,b4,b2,b1,b0;
       int sign=x>>31;
       x = (sign&~x)|(~sign&x);       //如果x为正则不变,否则按位取反(这样好找最高位为1的,原来是最高位为0的,这样也将符号位去掉了)
    
       // 不断缩小范围
       b16 = !!(x>>16)<<4;           //高十六位是否有1
       x = x>>b16;                   //如果有(至少需要16位),则将原数右移16位
       b8 = !!(x>>8)<<3;             //剩余位高8位是否有1
       x = x>>b8;                    //如果有(至少需要16+8=24位),则右移8位
       b4 = !!(x>>4)<<2;             //同理
       x = x>>b4;
       b2 = !!(x>>2)<<1;
       x = x>>b2;
       b1 = !!(x>>1);
       x = x>>b1;
       b0 = x;
       return b16+b8+b4+b2+b1+b0+1;  //+1表示加上符号位
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    题意:对于整数x,输出用补码表示x,最少需要多少二进制位。
    这道题应该是DataLab里最难的一道题了,我一开始是几乎没有思路的,容易知道其实这道题就是求x的二进制表示中,最高位的1在第几位,同时对于负数考虑符号位,但之后就不会做了。学习了别人的思路之后才明白了一点。类似二分查找。具体见代码。

    第二部分是浮点运算,这部分放宽了对程序的限制,现在可以使用逻辑运算符’&&‘,’||',以及if语句和while语句了。

    题目十一:floatScale2

    /* 
    /* 
    * 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 exp = (uf & 0x7f800000) >> 23;
       int sign = uf & (1<<31);
       if(exp==0)
           return uf<<1 | sign;
       if(exp==255)
           return uf;
       exp = exp + 0x1;
       if(exp==255)
           return 0x7f800000 | sign;
       return (exp << 23) | (uf & 0x807fffff);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    要求:输出浮点数2.0*x。其中返回值都以unsigned的形式给出,但它们都会被解释称float的位模式。
    思路:如果不考虑0,溢出和NAN的情况,实际上只需要把原浮点数的阶码+1即可。

    题目十二:floatFloat2Int

    /* 
     * 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 exp = ((uf & 0x7f800000)>>23) + (~0x7f + 0x1);
        int M = uf & ((1 << 23) + (~0x1 + 1));
        int sign = uf & (1<<31);
    
        int flag = 1<<22, te = exp;
        int res = 0;
    
        if((exp) > 30)
        {
            return 0x80000000u;
        }
        else if(exp < 0)
        {
            return 0x0;
        }
        while(flag && te)
        {
            if(M & flag)
            {
                res = res + (1 << (te-1));
            }
            flag = flag >> 1;
            te = te + (~0x1 + 1);
        }
        res = res + (1<
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    题意:对于浮点数 f 的位模式(用unsigned类型给出),返回(int)f的值。
    这道题倒不是很难,就是有点复杂。首先需要依据阶码,判断0的情况以及INF的情况;随后就是将unsigned类型给出的位模式,化成1.xxxxxx…x × \times × 2 e x p 2^{exp} 2exp的形式,写一个类似二进制数转十进制的程序。我自己最初就像平时写进制转换程序那样写了一个while循环,但是在看了别人的思路后发现其实不用循环,因为数字已经是用位模式表示出来的了,用移位就可以实现转换。

    题目十三:floatPower2

    /* 
     * 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) {
        if(x < -126)
        {
            return 0;
        }
        else if(x > 127)
        {
            return (0x7f << 24) + (0x1 << 23);
        }
        return (x + 0x7f) << 23;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    没想到最后一道题反而比较简单,这道题要求输出 ( 2.0 ) x (2.0)^x (2.0)x。当无法表示时按情况输出 0 0 0 + I N F +INF +INF。根据 I E E E 754 IEEE754 IEEE754标准很容易得到答案。所有 2 x 2^x 2x的数字都可以表示为
    1.00......0 × 2 e x p 1.00......0\times2^{exp} 1.00......0×2exp
    即位模式
    0   x x x x x x x x   00000000000000000000000 0\ xxxxxxxx\ 00000000000000000000000 0 xxxxxxxx 00000000000000000000000
    将x加上偏移量后,移位到阶码处即可。

    实验结果

    断断续续做了几天,终于把DataLab做完了。贴一个DataLab满分截图。
    在这里插入图片描述

  • 相关阅读:
    有关git commit --amend的用法及若干个问题
    FPGA信号处理系列文章——相关与卷积
    PyG-GCN-Cora(在Cora数据集上应用GCN做节点分类)
    MySQL入门指南4(查询进阶,外连接)
    链霉亲和素修饰聚苯乙烯微球,streptavidin修饰聚苯乙烯微球
    开发一个接口,需要考虑什么
    Eureka 高可用
    Android akptool 安装 mac 电脑
    基于elementui input完成的输入控件
    公司新来了个拿25K的测试,一介绍,原来是测试天花板级别的···
  • 原文地址:https://blog.csdn.net/Berserker____/article/details/127702617