CSAPP Data Lab

这篇博客详细介绍了如何使用位运算来解决计算机科学中的各种问题,包括求异或、最小补码整数、最大值判断、奇数位检查、取反、ASCII字符判断、条件表达式模拟、小于等于比较、逻辑非实现、位宽计算、浮点数乘2和转换为整数等。通过这些函数的实现,深入理解了位运算和浮点数在二进制表示中的应用。
摘要由CSDN通过智能技术生成

CS:APP Data Lab

前言

data lab主要考察位运算,补码,浮点数等相关内容,此lab能检验对整型,浮点数存储方式的理解,以及位运算的各种妙用,能够对整数浮点数的实际使用的过程中,对可能出现的“怪异”结果知其所以然。

以下13个函数即是本lab需要完成的内容,根据函数上方的注释及可用操作符限制补充完函数即可,部分函数不允许任何超过8bit的常量,详细内容查看相关注释。
在这里插入图片描述

lab资源下载点这里

对于一些可能用到或了解的知识在最下方列出。

lab

  1. bitXor

    /* 
     * bitXor - x^y using only ~ and & 
     *   Example: bitXor(4, 5) = 1
     *   Legal ops: ~ &
     *   Max ops: 14
     *   Rating: 1
     */
    // 求x^y
    // 思路,先按位与求出x和y同为1的位,然后x,y分别和~c按位与,求出各自不同时为1的位a,b
    // 最后分别对a,b取反,使得各自位上同时为0或同时为1的位设为1,再将两者按位与后再取反,即得出不同时为1的位
    int bitXor(int x, int y) {  // x:4  y:5
        int c = x & y;          // 0100
        int a = x & ~c;         // 0000
        int b = y & ~c;         // 0001
        int ret = ~a & ~b;      // 1110
        return ~ret;            // 0001
        
        // 或,不是同时为0和不是同时为1的两种情况进行按位与
        // return ~(~x & ~y) & ~(x & y);
    }
    
  2. tmin

    /* 
     * tmin - return minimum two's complement integer 
     *   Legal ops: ! ~ & ^ | + << >>
     *   Max ops: 4
     *   Rating: 1
     */
    // 求Tmin
    // 将1左移31位,这样除了最高符号位其余全为0,即可取得最小值
    int tmin(void) {
        return 1 << 31;
    }
    
  3. isTmax

    /*
     * isTmax - returns 1 if x is the maximum, two's complement number,
     *     and 0 otherwise 
     *   Legal ops: ! ~ & ^ | +
     *   Max ops: 10
     *   Rating: 1
     */
    // 判断x是否是Tmax
    // 若x是Tmax,那么x+1=Tmin,而~Tmin=Tmax,所以可以以此验证,此外当x=-1时,由于溢出也满足这个等式,还需要特判一下
    // 利用异或和逻辑非来判断等式是否成立,!!(x+1)来判断x是否为-1,!!是将数字转化为true或false
    int isTmax(int x) {
        return !(~(x + 1) ^ x) & !!(x + 1);
    }
    
  4. 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
     */
    // 求x的二进制奇数位上全为1
    // 如果奇数位上全为1,返回true
    int allOddBits(int x) {
        int a, b;
        a = 0xAA + (0xAA << 8);  // 注意运算符优先级
        a= a + (a << 16);  //  a的奇数位全为1,偶数位为0
        b = a & x;  //  如果x的奇数位上也全位1,等式的结果为a
        return !(a ^ b);    // 用异或和逻辑非判断两者是否相等
    }
    
  5. negate

    /* 
     * negate - return -x 
     *   Example: negate(1) = -1.
     *   Legal ops: ! ~ & ^ | + << >>
     *   Max ops: 5
     *   Rating: 2
     */
    // 求x的相反数
    // 补码的非=补码取反+1 , -x=~x+1
    int negate(int x) {
        return ~x + 1;
    }
    
  6. isAsciiDigit

    /* 
     * 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
     */
    // 求x是否在0x30~0x39之间
    // 即 00110000~00111001之间,发现4~5位固定是1,之后全0,
    // 而0~3位则从0000递增变化到1001,所以我们先判断高位的24个bit是否为0x3(a)
    // 再判断第3位是否为1(b),若为1,则判断1,2位上是否有1(c),利用b和c的按位与即可判断
    int isAsciiDigit(int x) {
        int a = !(x >> 4 ^ 3);	// 高位的24个bit是否为0x3
        int b = !!(x & 8);		// 第3位是否为1
        int c = !!(x & 6);		// 则判断1,2位上是否有1
        return a & !(b & c);	// 若高24bit为0x3,且低4位不超过0x9,return 1
    }
    
  7. conditional

    /* 
     * conditional - same as x ? y : z 
     *   Example: conditional(2,4,5) = 4
     *   Legal ops: ! ~ & ^ | + << >>
     *   Max ops: 16
     *   Rating: 3
     */
    // 模拟三元运算符
    // 使用!x-1,即可将x的真假翻转,-1用~0来实现
    int conditional(int x, int y, int z) {
        int a = !x + ~0;    // 若x是0,a=0,否则a是全1
        return (a & y) | (~a & z);
    }
    
  8. isLessOrEqual

    /* 
     * isLessOrEqual - if x <= y  then return 1, else return 0 
     *   Example: isLessOrEqual(4,5) = 1.
     *   Legal ops: ! ~ & ^ | + << >>
     *   Max ops: 24
     *   Rating: 3
     */
    // 模拟<=
    // if x<=y ,返回1,否则返回0
    // 通过y-x的正负即可判断,但作差可能导致溢出,需要特殊考虑一下
    int isLessOrEqual(int x, int y) {
        int sign_x = x >> 31 & 1;   // x的符号位
        int sign_y = y >> 31 & 1;   // y的符号位
        int a = y + (~x + 1);          // y - x
        int sign_a = a >> 31 & 1;   // a的符号位
        // 若a最高位是0,即y-x>=0,应该返回1,但若x>0,y<0,那此时是负溢出造成的结果,应当返回0
        // 若a最高位是1,即y-x<0,应该返回0,但若x<0,y>0,那此时是正溢出造成的结果,应当返回1
        // 总上,返回1有两种可能,x,y符号相同且a最高位是0,以及x<0,y>0(符号位无所谓,x负y正,返回1即可)
        int nosame_xy = sign_x ^ sign_y;  // 若x,y符号位不同为则为1
        return (!sign_a & !nosame_xy) | (nosame_xy & sign_x);
    }
    
  9. 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 
     */
    // 实现逻辑非,0返回1,非0返回0
    // 只有0的相反数是其本身,所以x^(-x) 只有当x为0时,结果为0,否则,结果是负数,最高位为1,
    // 同时需要注意,在补码中,-Tmin=Tmin,需要特判
    // 所以将最高位提取出来与1异或即可
    // 如果最高位是1,返回0,如果最高位是0,且x^(-x)的最高位是1,返回0
    int logicalNeg(int x) {
        int sign_x = (x >> 31 & 1) ^ 1;   // 如果符号位是1,等式结果为0,否则为1
        int sign_xor = ((~x + 1) ^ x) >> 31 & 1;    //提出 x^(-x)的最高位
        return sign_x & (sign_xor ^ 1);     // ^1 是为了翻转最低位
    
        // 或,上面的用了10个ops,显然冗余了一些,简化如下
        // 对于(~x+1)>>31,若x是正数,结果为-1,若x是负数数或0,结果是0,也就不用x^(-x)来判断了,但也需要特判
        // 用 x|(~x+1>>31) 来表示,若结果为-1,则x不为0,若结果为0,则x为0。最后加上1返回即可
        // return ((x | (~x + 1)) >> 31) + 1;
    }
    
  10. 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
     */
    // 求x用二进制补码表示最少需要几位
    // 若x为正数,显然求出其最左边的1的处于第几位,再加1表示符号位即可
    // 若x为负数,则求出其最左边的0的位置,再加1表示符号位即可
    //      为啥负数是找最左边的0呢,因为最高位符号位的权值,如11011(-5)而,1011(-5)
    //      可以发现,假设有w+1位,若符号位的紧邻右边的位也是1的话,先不管其余位,那么值为-(2^w)+(2^(w-1))=-(2^(w-1))
    //      也就是说,与其1100...00,不如100...00这样表示,能使得位数更小,符号位紧邻右边有连续的1同理
    //      我们不妨对x去反,这样就可以和正数一样找最左边的1
    // 那么怎么找最左边的1呢,二分思想,一开始我们先看原x的高16位是否有1,若有则将x>>16,相当于砍掉x的低16位
    // 若没有,则说明高16位全位0,不动,相当于砍掉x的高16位,以此8,4,2进行下去
    int howManyBits(int x) {
        int b16,b8,b4,b2,b1,b0;
        int sign= x >> 31;  
        // 若x是正数不变,若x是负数,按位取反,| 左边是为了应对负数,|右边是为了应对正数,各自的另一半则为0
        x = (sign & ~x) | (~sign & x);  
    
        // 二分思想
        b16 = !!(x >> 16) << 4; //若高16位有1,二进制位的4位置为1,权值2^4=16
        x = x >> b16;           // 若有,此表示式将x右移16位,否则不变
        b8 = !!(x >> 8) << 3;   // 同理,高8位是否有1
        x = x >> b8;
        b4 = !!(x >> 4) << 2;   // 高4位是否有1
        x = x >> b4;
        b2 = !!(x >> 2) << 1;   // 高2位是否有1
        x = x >> b2;
        b1 = !!(x >> 1);        // 最1位是否有1
        x = x >> b1;            
        // 上面所谓的高几位是原x不断砍半后的二进制位,b0为了检验移位到最后剩下的两个bit位的最低位是否为1
        b0 = x;             
        return b16 + b8 + b4 + b2 + b1 + b0 + 1;    //记得加上符号位
      
    }
    
  11. 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
     */
    // 计算2*f,f是浮点数,不过参数和返回值都是无符号整形表示
    // 首先,对于NaN,不用计算,直接返回,NaN的阶码部分全位1,尾数部分不为0,
    // 先求阶码exp=(uf0x7f800000)>>23  ,再求尾数 frac=uf&0x7fffff
    //   如果exp=255,并且尾数非0,那就是NaN,return uf即可,如果frac全0,表示无穷大,*2还是无穷大,也return uf即可
    //   如果exp=0,非规格化形式,若frac=0,表示值为0,若frac!=0,表最接近0的那些数,可以统一处理为尾数*2,即frac<<=1,不用担心
    //   如果exp!=0 && exp!=255,规格化形式,只需要把exp+1,即可
    // 曾想过,为什么非规格化且不为0的时候为什么不能是exp+1,毕竟uf*2,不就是阶码+1,但非规格化和规格化一样处理的话
    // exp+1后,就从非规格化变为了规格化,但float最小的非规格化为1.4*e-45,最小的规格化为1.2*e-38,
    // 显然比较小的非规格化按这种方式处理后,一跃变为了规格化,变大了不止两倍。
    unsigned floatScale2(unsigned uf) {
        unsigned exp = (uf & 0x7f800000) >> 23;
        unsigned frac = uf & 0x7fffff;
        unsigned sign = uf >> 31 & 1;
        unsigned res;
        if(exp == 0xff) {
            return uf;
        }else if(exp == 0){
            frac <<= 1;
            res = sign << 31 | frac;    // 本应还要与上exp<<23,但此时exp全0就每必要了
        }else{
            ++exp;
            res = sign << 31 | exp << 23 | frac;
        }
        return res;
    }
    
  12. 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型
    // 如果原浮点数值为0,return 0,
    // 若指数大于31,溢出,返回0x80000000
    // 若指数小于0,返回0
    // 否则,把小数部分看为整数(即相当于小数部分整体左移了23位),再根据exp的大小,对小数部分进行最终调整
    //    若exp>23,还得左移exp-23,若exp<23,说明左移多了,还得右移23-exp位
    //    最后根据符号位进行正负调整,返回即可
    //
    int floatFloat2Int(unsigned uf) {
        int sign = uf >> 31 & 1;    // 取出符号位
        int exp = ((uf & 0x7f800000) >> 23) - 127;  // 指数
        int frac = (uf & 0x007fffff) | 0x00800000;  // 小数,且加上了1
        // 若uf为0或指数小于0,返回0
        if(!(uf & 0x7fffffff) || exp<0) return 0;
        // 若指数大于31,溢出
        if(exp >= 31) 
            return 0x80000000;
        else if(exp > 23)  // 右移少了
            frac <<= exp - 23;
        else            // 右移多了
            frac >>= 23-exp;
        
        if(sign) frac = -frac;
    
        return frac;
    }
    
  13. 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
     */
    // 计算2.0^x
    // 浮点数标准用V=(-1)^s * M * 2^E
    // 根据浮点数表示,x就是exp部分
    // 根据int的范围进行讨论即可
    // 单精度exp占8位,阶码E=e-Bias(2^7-1) ,即范围是[-126,127]
    // 若 x>=128,超过所能表示的最大值了,+INF
    // 若 >=-127, 在指数表示范围内,直接放入exp部分
    // 若 x>=-150,exp部分能放127,frac有23位,根据标点数frac的含义,也可以用来表示,所以只要不小于150,都可以表示出来
    // 若 x<150, 超出最小的表示范围了,return 0
    unsigned floatPower2(int x) {
        if(x >= 128) return 0x7f800000;
        if(x >= -126) return (x + 127) << 23;
        if(x >= -150) return 1 << (x+150);
        return 0;
    }
    
    

    上方所有函数经测试结果如下
    在这里插入图片描述

note

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值