• 【CSAPP】Data Lab实验笔记


    前天讲到要刚CSAPP,这一刚就是两天半.CSAPP果然够爽,自带完整的说明文档,评判程序,辅助程序.样例直接百万组走起,管饱!
    datalab讲的是整数和浮点数怎么用二进制表示的,考验的是用基本只用位运算来实现一些看似稀松平常的操作.实际体会就是看题五秒钟,脱发两小时.当然做出来的成就感也是爆棚的.

    求异或

    根据离散数学等值演算写出公式

    //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的数就是了

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

    求补码可表示的最大数

    这题开始需要一个技巧:!!x可以把非零数变成1,把0变成0
    tmax=0x7FFFFFFF
    tmax+1=0x10000000
    (tmax+1)+(tmax+1)=0x00000000
    C语言形式写作: x=tmin()-1
    变形得 x+1=tmin()
    a ^ b仅在每一位都相同时为假,则可取反用!(a ^ b)表示仅在每一位都相同时为1
    最后额外排除0xFFFFFFFF

    //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) {
      return !((x+1)+(x+1))&!!(x+1);
    }
    

    判断所有偶数位是否均为1

    令y=1010...1010(二进制),即y==0xAAAAAAAA
    运用<<和+来让0XAA重复四次得到该值
    当且仅当x的任意偶数位都为1时返回1
    即x存在偶数位为0时返回0
    即结果为!(~x&0XAAAAAAAA)

    /* 
     * 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) {
      int y=0xAA;
      y=(y<<8)+y;
      y=(y<<16)+y;
      return !(~x&y);
    }
    

    求-x

    根据公式直接写

    /* 
     * negate - return -x 
     *   Example: negate(1) = -1.
     *   Legal ops: ! ~ & ^ | + << >>
     *   Max ops: 5
     *   Rating: 2
     */
    int negate(int x) {
      return (~x+1);
    }
    

    判断是否为'0'~'9'

    !(a^b)在当且仅当a与b任意一位都相同时为1,可以代替==
    查表得规律:
    00...011100X表示38~39
    00...0110XXX表示30~37
    故借助于<<分别判断之

    //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) {
      return !((x>>1)^0x1C) | !((x>>3)^0x6);
    }
    

    实现x?y:z

    假设存在操作@,
    x=1时有x@y=y
    x=0时有x@y=0
    联想到&的性质可知
    x=111...111(二进制)时x&y=y
    x=000...000(二进制)时x&y=0
    而111...111(二进制)=0XFFFFFFFF=-1
    再根据前面求负数的题,~a+1=-a

    /* 
     * 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 ((~!!x+1)&y)|(~!x+1&z);
    }
    

    判断小于等于

    直接求y-x在异号的时候会溢出
    (x>>31)^(y>>31)判断xy符号位是否不同
    sub=y-x=y+~x+1求差值
    若同号,y-x<0时返回1
    若异号,x<0时返回1

    /* 
     * 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 sx=x>>31;
      int sy=y>>31;
      int diff=sx^sy;
      int sub=(y+~x+1)>>31;
      return ((!diff&!sub)|(diff&sx))&1;
      /*
      另一种方法:分x>=0?,y>=0?四种情况讨论
      int xb=x>>31;
      int xa=~xb;
      int yb=y>>31;
      int ya=~yb;
      return (xa&((ya&!((y+~x+1)>>31))))|(xb&((ya&1)|(yb&!!((~y+x)>>31))));
      */
    }
    

    另一种思路:分x>=0?,y>=0?四种情况讨论

    int isLessOrEqual(int x, int y) {
      int xb=x>>31;
      int xa=~xb;
      int yb=y>>31;
      int ya=~yb;
      return (xa&((ya&!((y+~x+1)>>31))))|(xb&((ya&1)|(yb&!!((~y+x)>>31))));
    }
    

    实现!运算符

    关键知识点:当且仅当x==0时,x与-x符号位相同,并且值为0
    故判断~(-x|x)的符号位即可

    //4
    /* 
     * 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+1)|x)>>31)&1;
    }
    

    另一种思路:我管这叫二分折叠

    int logicalNeg(int x) {
      int a=(x>>16)|x;
      int b=(a>>8)|a;
      int c=(b>>4)|b;
      int d=(c>>2)|c;
      int e=(d>>1)|d;
      return (~e)&1;
    
    }
    

    判断位数

    看Max ops知难度.
    预处理:
    把负数转换成它的~,使得正负数可用同一套操作
    每个数都有一个符号位,所以计数器初始值为1
    之后二分法:
    判断高十六位是否为0,是的话把计数器加16,然后截取高十六位作为新的x;
    否则节取低十六位作为新的x
    判断高八位是否为0,是的话把计数器加8,然后截取高八位作为新的x;
    否则节取低八位作为新的x
    ......
    这个不断判断的过程可以借用前面x?y:z的代码,最后做一点点化简,防止超90ops

    /* 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 ret=0;
      int h16,h8,h4,h2,h1;
      int sign=(x>>31)&1;
      x=((~!!sign+1)&(~x))|(~!sign+1&x);
      ret=1;
      h16=(x>>16);
      ret+=((~!!h16+1)&16);
      x=h16|(~!h16+1&((x<<16)>>16));
      h8=x>>8;
      ret+=((~!!h8+1)&8);
      x=h8|(~!h8+1&((x<<8)>>8));
      h4=x>>4;
      ret+=((~!!h4+1)&4);
      x=h4|(~!h4+1&((x<<4)>>4));
      h2=x>>2;
      ret+=((~!!h2+1)&2);
      x=h2|(~!h2+1&((x<<2)>>2));
      h1=x>>1;
      ret+=((~!!h1+1)&1);
      ret+=!!x;
      return ret;
    

    进一步化简后:

    int howManyBits(int x) {
      int b16,b8,b4,b2,b1;
      int ret=0;
      int sign=(x>>31);
      x=(~sign&x)|(sign&~x);
      ret=1+!!x;
      b16=!!(x>>16)<<4;
      x=x>>b16;
      b8=!!(x>>8)<<3;
      x=x>>b8;
      b4=!!(x>>4)<<2;
      x=x>>b4;
      b2=!!(x>>2)<<1;
      x=x>>b2;
      b1=!!(x>>1);
      return b16+b8+b4+b2+b1+ret;
    }
    

    求浮点数f*2

    if-else解封了还有啥难得?分类讨论,一个坑点在于无符号数右移的时候是无符号右移

    //float
    /* 
     * 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) {
      unsigned e=(uf<<1)>>24;
      if(e==0xFF) return uf;
      else if(e==0) return (uf<<1)+(uf&0x80000000);
      else return uf+(1<<23);
    }
    

    float转int

    注意了<<和>>都不能超过自身的范围,也不能用负数,否则可能发生不可预期的错误

    /* 
     * 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) {
      unsigned f=((uf<<11)>>11)+(1<<23);
      unsigned e=(uf<<1)>>24;
      int sign=(uf>>31)&1?-1:1;
      int shift=e-127-23;
      if(e==0xFF) return 0x80000000;
      else if(e==0) return 0;
      else{
        if(shift<-23) return 0;
        else if(shift>10) return 0x80000000;
        else if(shift<0) return (f>>(-shift))*sign;
        else return (f<<shift)*sign;
      }
    }
    

    求2.0^x

    这题我竟然超时了.原因是我是在移动硬盘上装得linux,而评判程序恰好时运行在本地的,这样子速度就慢了一截,结果就超时了.

    /* 
     * 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<=-127) return 0;
        if(x>=128) return 0x7f800000;
        return (127+x)<<23;
    }
    
  • 相关阅读:
    c# 基础
    摹客插件,自动识别画板大小!
    知道这10点,你才是真正会画线框图
    在线原型实例(可编辑):图片社交-InstagraAPP
    16 种原型设计工具及其使用场景
    微博APP在线原型实例(可编辑)
    5款前端切图工具大比拼:谁是最强切图神器
    你真的了解这4款协作设计吗
    nvm 安装及使用(npm版本管理工具)
    webpack入门四 安装vue,并打包
  • 原文地址:https://www.cnblogs.com/kangyupl/p/13022465.html
Copyright © 2020-2023  润新知