深入理解計算機第二次實驗(LAB2-datalab)

LAB2-datalab

先貼出啦本次實驗代碼,rating 1,2,3的部分就不寫分析了,看簡單的註釋應該看的懂。
主要是部分rating4的。
先貼代碼

/* 
 * CS:APP Data Lab 
 * 
 * <Please put your name and userid here>
 * YuanYou shi , 201708010303
 * bits.c - Source file with your solutions to the Lab.
 *          This is the file you will hand in to your instructor.
 *
 * WARNING: Do not include the <stdio.h> header; it confuses the dlc
 * compiler. You can still use printf for debugging without including
 * <stdio.h>, although you might get a compiler warning. In general,
 * it's not good practice to ignore compiler warnings, but in this
 * case it's OK.  
 */

#if 0
/*
 * Instructions to Students:
 *
 * STEP 1: Read the following instructions carefully.
 */

You will provide your solution to the Data Lab by
editing the collection of functions in this source file.

INTEGER CODING RULES:
 
  Replace the "return" statement in each function with one
  or more lines of C code that implements the function. Your code 
  must conform to the following style:
 
  int Funct(arg1, arg2, ...) {
      /* brief description of how your implementation works */
      int var1 = Expr1;
      ...
      int varM = ExprM;

      varJ = ExprJ;
      ...
      varN = ExprN;
      return ExprR;
  }

  Each "Expr" is an expression using ONLY the following:
  1. Integer constants 0 through 255 (0xFF), inclusive. You are
      not allowed to use big constants such as 0xffffffff.
  2. Function arguments and local variables (no global variables).
  3. Unary integer operations ! ~
  4. Binary integer operations & ^ | + << >>
    
  Some of the problems restrict the set of allowed operators even further.
  Each "Expr" may consist of multiple operators. You are not restricted to
  one operator per line.

  You are expressly forbidden to:
  1. Use any control constructs such as if, do, while, for, switch, etc.
  2. Define or use any macros.
  3. Define any additional functions in this file.
  4. Call any functions.
  5. Use any other operations, such as &&, ||, -, or ?:
  6. Use any form of casting.
  7. Use any data type other than int.  This implies that you
     cannot use arrays, structs, or unions.

 
  You may assume that your machine:
  1. Uses 2s complement, 32-bit representations of integers.
  2. Performs right shifts arithmetically.
  3. Has unpredictable behavior when shifting an integer by more
     than the word size.

EXAMPLES OF ACCEPTABLE CODING STYLE:
  /*
   * pow2plus1 - returns 2^x + 1, where 0 <= x <= 31
   */
  int pow2plus1(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     return (1 << x) + 1;
  }

  /*
   * pow2plus4 - returns 2^x + 4, where 0 <= x <= 31
   */
  int pow2plus4(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     int result = (1 << x);
     result += 4;
     return result;
  }

FLOATING POINT CODING RULES

For the problems that require you to implent floating-point operations,
the coding rules are less strict.  You are allowed to use looping and
conditional control.  You are allowed to use both ints and unsigneds.
You can use arbitrary integer and unsigned constants.

You are expressly forbidden to:
  1. Define or use any macros.
  2. Define any additional functions in this file.
  3. Call any functions.
  4. Use any form of casting.
  5. Use any data type other than int or unsigned.  This means that you
     cannot use arrays, structs, or unions.
  6. Use any floating point data types, operations, or constants.


NOTES:
  1. Use the dlc (data lab checker) compiler (described in the handout) to 
     check the legality of your solutions.
  2. Each function has a maximum number of operators (! ~ & ^ | + << >>)
     that you are allowed to use for your implementation of the function. 
     The max operator count is checked by dlc. Note that '=' is not 
     counted; you may use as many of these as you want without penalty.
  3. Use the btest test harness to check your functions for correctness.
  4. Use the BDD checker to formally verify your functions
  5. The maximum number of ops for each function is given in the
     header comment for each function. If there are any inconsistencies 
     between the maximum ops in the writeup and in this file, consider
     this file the authoritative source.

/*
 * STEP 2: Modify the following functions according the coding rules.
 * 
 *   IMPORTANT. TO AVOID GRADING SURPRISES:
 *   1. Use the dlc compiler to check that your solutions conform
 *      to the coding rules.
 *   2. Use the BDD checker to formally verify that your solutions produce 
 *      the correct answers.
 */


#endif
/* 
 * bitAnd - x&y using only ~ and | 
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
int bitAnd(int x, int y) { // & = ~(~ | ~)
  x = ~x;
  y = ~y;
  x = x | y;
  return ~x;
}
/* 
 * getByte - Extract byte n from word x
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByte(0x12345678,1) = 0x56
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2
 */
int getByte(int x, int n) { // wei yi :  x >> 2^3n yan ma 0xff(255)
  n = n << 3;
  x = x >> n;
  x = x & 0xff;
  return x;
}
/* 
 * logicalShift - shift x to the right by n, using a logical shift
 *   Can assume that 0 <= n <= 31
 *   Examples: logicalShift(0x87654321,4) = 0x08765432
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3 
 */
int logicalShift(int x, int n) {
  /*x = x >> n;*/
  int yama = 0x1 << 31;
  yama = yama >> n;
  yama = ~(yama << 1);
  return (x >> n)&yama;
}
/*
 * bitCount - returns count of number of 1's in word
 *   Examples: bitCount(5) = 2, bitCount(7) = 3
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
00000001 00000001 00000001 00000001
10011010

 */
int bitCount(int x) {// bu dui 
/*  int varCounter = 0;
  int counter = 32;
  while(counter--){
    varCounter += x & 1;
    x = x >> 1;
  }
  return varCounter;*/
  int table = ( ( (0x01 << 8) | 0x01) << 8 | 0x01 ) << 8 | 0x01;
  int counter = x & table;
  counter += (x>>1) & table;
  counter += (x>>2) & table;
  counter += (x>>3) & table;
  counter += (x>>4) & table;
  counter += (x>>5) & table;
  counter += (x>>6) & table;
  counter += (x>>7) & table;
  counter = counter + (counter >>16);
  counter = counter + (counter >>8);
  return counter & 0xff;
}
/* 
 * bang - Compute !x without using !
 *   Examples: bang(3) = 0, bang(0) = 1
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 4 
 */
int bang(int x) { 
  x = (~x+1) | x;
  return (~(x >> 31)) & 0x1;
}
/* 
 * tmin - return minimum two's complement integer 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmin(void) {
  return 0x1<<31;
}
/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
	eg (5,3) : 0000 0101 => 0
	           1111 1011 
	eg (4,3) : 0000 0100 => 1
                   1111 1100
	eg (7,4) : 0000 0111 => 1
		   1111 1001
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 *   
 */
int fitsBits(int x, int n) {
  int left = 32 + ( ~n + 1 ) ;// 32 - n
  int tempx = x << left;
  tempx = tempx >> left; // kan (32-n) shi bu shi fu hao
  return !(x ^ tempx);
}
/* 
 * divpwr2 - Compute x/(2^n), for 0 <= n <= 30
 *  Round toward zero
 *   Examples: divpwr2(15,1) = 7, divpwr2dic(-33,4) = -2
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int divpwr2(int x, int n) {
    /*return x>>n;*/
/*  int yama = x >> 31;
  int tempx = ~x + 1;
  return ( (yama &( (~(tempx >> n)+1) |0x80000000) ) ) | (~yama & (x >> n) );*/
  int yanma = x >> 31;
  x += yanma & ((1 << n) + ~0) ;
  return x >> n;
}
/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  return ~x+1;
}
/* 
 * isPositive - return 1 if x > 0, return 0 otherwise 
 *   Example: isPositive(-1) = 0.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 3
 */
int isPositive(int x) {
  return !((x>>31) | !x);
}
/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
	return (y-x)>0 => ( y+(~x+1) ) > 0
	kao lv zheng fu yi chu
	zheng yi chu return 1
	fu yi chu return 0
 */
int isLessOrEqual(int x, int y) { // bu tai d
/*  y = y + (~x+1);
  return !(y >> 31);*/
/*  int sign =! ( ((~x+1)>>31) ^ (y >> 31) );
  int zheng = sign & !(y >> 31);
  int fu = sign & (y >> 31);
  int tempy = y + (~x+1);
  fu = fu & !(tempy>>31);
  zheng = zheng & (tempy>>31);
  return (zheng | !fu & !(y>>31) | !(y ^ x));
*/
int sy = (y>>31);
int sign = (x >> 31) ^ sy;
int tempy = y + (~x+1);
return ( (sign & (x>>31)&1 ) | (!sign & !((tempy>>31)&1) ) | (!(x^y)) );
}
/*
 * ilog2 - return floor(log base 2 of x), where x > 0
 *   Example: ilog2(16) = 4
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 90
 *   Rating: 4
32>n>=0
0     0     0     0     0
1     1     1     1     1 00000
16    8     4     2     1
ffff  ff    f     3     1

dui ying xiang jia
 */
int ilog2(int x) {
  int table = 0x0;

  int sign = !!(x >> 16);
  table += sign << 4;
  x = x >> (table & 16);

  sign = !!(x >> 8);
  table += sign << 3;
  x = x >> (table & 8);

  sign = !!(x >> 4);
  table += sign << 2;
  x = x >> (table & 4);

  sign = !!(x >> 2);
  table += sign << 1;
  x = x >> (table & 2);

  sign = !!(x >> 1);
  table += sign ;

  return table;
}
/* 
 * float_neg - Return bit-level equivalent of expression -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 representations of
 *   single-precision floating point values.
 *   When argument is NaN, return argument.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 10
 *   Rating: 2
unsigned result;
    unsigned tmp;
    result=uf ^ 0x80000000; // 
    tmp=uf & (0x7fffffff);
     if(tmp > 0x7f800000)// nan
        result = uf;
    return result;
 */
unsigned float_neg(unsigned uf) {
  unsigned s = (~uf)&0x80000000;
  unsigned exp = uf & (0x7fffffff);
  if(exp > 0x7f800000)// nan
    return uf;
  else
    return uf & 0x7fffffff | s;
}
/* 
 * float_i2f - Return bit-level equivalent of expression (float) x
 *   Result is returned as unsigned int, but
 *   it is to be interpreted as the bit-level representation of a
 *   single-precision floating point values.
 *   Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
 *   Max ops: 30
 *   Rating: 4


0x1234
00000000 00000000 00010010 00110100
1.00100010
10010001 10100000 00000000 00000000
00100011 01000000 00000000 00000000
                  00000001 11111111
                  00000001 00000000
                  00000011 00000000
00000000000000000000000000000000011


1111111 11111111 1111111 1 11111111
00000000
11111111
1 10000000


	        s     exp                    frac
	float : 1   11111111      11111111 11111111 1111111
                0   10001011      00100011 01000000 0000000
        exp = frac.length + 127

 */
unsigned float_i2f(int x) {
  if (!x)
    return 0;
  unsigned sign = x &(0x1<<31);
  unsigned frac=x, tmp=0, flag=0,exp=0;
  if(x < 0){
    frac = -x;
  }
  while (1)
  {
    tmp=frac;
    frac<<=1;
    exp++;
    if (tmp & 0x80000000) break;
  }

  if ((frac & 0x01ff)>0x0100)
    flag=1;
  else if ((frac & 0x03ff)==0x0300)
    flag=1;
  return sign + ((159-exp)<<23) + (frac>>9) + flag;
}
/*
 *   float_twice - 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
	        s     exp                    frac
	float : 1   11111111      11111111 11111111 1111111
	s    = 8000 0000
    exp  = 7f80 0000
	frac = 007f ffff
 */
unsigned float_twice(unsigned uf) {
  unsigned s = 0x80000000 & uf;
  unsigned exp =  0x7f800000 & uf;
  unsigned frac = 0x007fffff & uf;
  if(!exp){
    uf = s | (frac << 1);
  }else if(exp != 0x7f800000){
    uf += 0x00800000;
  }
  return uf;
}

簡述rating4

bang

Rating 4 的題目如bang,需要知道的是2進制補碼的一些特徵。對於任何一個x(除零外)x和-x中必然有一個數的符號位爲1.所以直接比較x|-x的符號位即可。
int bang(int x) {
x = (~x+1) | x;
return (~(x >> 31)) & 0x1;
}

bitcount

對於題目bitCount 和 ilog2 其實本質上是一樣的,如果我們暴力便利一遍32位就很直接得到問題的解,但是既然題目有限制我們的操作數目,所以就要優化,如何在不用依次遍歷32次的情況就可以得到問題的解。
所以這部分其實就要靠算法的知識了。既然遍歷32遍很慢,我們就要考慮問題的突破口在哪裏?
bitCount題目是要統計1的個數,那麼我們最樸素的想法是每次看最低位是不是1,然後右移,直到移動完31次,那麼我們用分治的想法,如果把高16位和低16位分開,這樣每次右移就可同時判斷出兩個。問題再分解:把32位分成4個8位,當然也可繼續再分解,但是學過分治法就知道,算法的複雜度取決於 分解問題的複雜度 + 合併問題的複雜度 + 解決問題的複雜度。所以我們沒有必要再繼續分解。所以bitCount的函數很自然的就是如下:

int bitCount(int x) { 
  int table = ( ( (0x01 << 8) | 0x01) << 8 | 0x01 ) << 8 | 0x01;
  int counter = x & table;
  counter += (x>>1) & table;
  counter += (x>>2) & table;
  counter += (x>>3) & table;
  counter += (x>>4) & table;
  counter += (x>>5) & table;
  counter += (x>>6) & table;
  counter += (x>>7) & table;
  counter = counter + (counter >>16);
  counter = counter + (counter >>8);
  return counter & 0xff;
}

這裏有個巧妙的地方是算法思想中沒有體現的,就是我們用一個count的4個8bit作爲每段的計數器,這樣得到和的時候只需要“對摺”這個counter兩次就可以
相當於 count(32位) = counter1(8位) + counter2(8位) + counter3(8位) + counter4(8位)

ilog2

而ilog2也有着同樣巧妙的地方。
對於一個int型的x取以2爲底的對數。最大爲31,最小爲0。(因爲題目要求x的取值爲0x7fffffff~0x00000001)而這個題目的本質其實位最高位‘1‘在哪裏
這兩個特徵允許我們用一個5位長的位串(定義爲table)就可表示0到31的位就可以。如:10111(2)=23(10)這樣我們就很好做題了
依舊是二分法:

  1. 看高16位有沒有‘1‘,如果有的話table的第4爲置爲1,否則爲0
  2. 如果table的第4位爲1,則看高16位中的高8位是否有‘1‘,有的話table的第三位置爲1,否則爲0.如果table[4] = 0,則看低16位中的高8位是否有1。
  3. 依次類推,每次縮小範圍但優先看高位
    這樣問題規模小到查找的位串長爲1的時候結束。這樣能保證每次找到的是這個位串最高位。
    很自然的寫出代碼:
int ilog2(int x) {
  int table = 0x0;

  int sign = !!(x >> 16); // 查找最高位是否有1
  table += sign << 4;      // 找到有1,則把table的第4位置爲1
  x = x >> (table & 16);  //  找到有1,捨棄第16位,沒找到不捨

  sign = !!(x >> 8);
  table += sign << 3;
  x = x >> (table & 8);

  sign = !!(x >> 4);
  table += sign << 2;
  x = x >> (table & 4);

  sign = !!(x >> 2);
  table += sign << 1;
  x = x >> (table & 2);

  sign = !!(x >> 1);
  table += sign ;

  return table;
}

看到代碼中有些地方的處理,每次查詢table對應的位置是不是1就可以,最後table表示的就是結果,捨棄低位是爲了方便下次的查找,不需要判斷應該去哪半段找1,直接在當前已經截取好的位串中取高n位就可以。

float_i2f

float_i2f方法其實要簡單很多,不需要二分法,直接做就可以了,就是需要考慮一下float型的進位和捨棄的問題。
做這道題之前要清楚一個問題:float在計算機中是怎麼表示的。
一個32位的float中 有一個符號位,8位表示階碼,23位尾數
那麼一個int型的數如:0x1234轉換位float需要四步

(0x1234 = (000000000001001000110100)(int) = (0(s) 10001011(exp) 001000110100(frac) )(float)
  1. 確定符號位 s: 0
  2. 確定階碼exp:127 + frac.length(尾數的長)
  3. 確定尾數frac:捨棄最高位的1的後面
  4. 做舍入處理:如果第三步得到的frac長大於23,則作舍入處理。舍入原則如果把第23位和後面要舍入的直接加個小數點,若小數點之後大於0.5則入,若小於0.5則捨去,若等於0.5則考慮小數點前一位,使得攝入之後小數點前一位爲0。
unsigned float_i2f(int x) {
  if (!x)
    return 0;
  unsigned sign = x &(0x1<<31);
  unsigned frac=x, tmp=0, flag=0,exp=0;
  if(x < 0){
    frac = -x;
  }
  while (1)
  {
    tmp=frac;
    frac<<=1;
    exp++;
    if (tmp & 0x80000000) break;
  }

  if ((frac & 0x01ff)>0x0100)
    flag=1;
  else if ((frac & 0x03ff)==0x0300)
    flag=1;
  return sign + ((159-exp)<<23) + (frac>>9) + flag;
}

看到代碼
第一步特判,如果是零,直接返回。
這裏要注意,獲取exp和frac都是在while中(此時的frac在32中的最高位向低位的位置即31位到8位的位置,此時的exp是保存了int型的x在有效的數前有多少位的零,所以後面求exp的時候是127-(32 - exp) 也就是159-exp),而獲取符號在while之前。
最後一個if和else判斷則是判斷尾數舍入的問題。(如x=0x1234時,舍入之前frac 後8位到0位要是有數字就要判斷舍入,而此時舍入要求的0.5對應的恰好爲0x100。else if中的0x300 就是判斷是否是1.5,此時要進位)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章