深入理解计算机系统 实验作业

  1. 实验目的:本次实验是对位操作级别的练习,本次报告是对实验中bits.c文件的再次说明,目的是帮助理解位级别的操作和表达以及培养同学们写代码之前先思考清楚思路的良好习惯,需要同学们认真完成
  2. 报告要求:本次报告需要同学们把对应实验中的函数逐一进行分析说明,写出实现的依据,也就是推理过程,可以是一个简单的数学证明,也可以是代码分析,根据实现中你的想法不同而异。请同学们耐心独立完成。


/*
 * CS:APP Data Lab
 *
 * bits.c - Source file with your solutions to the Lab.
 *          This is the file you will hand in to your instructor.
 *
 */
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "tests.h"
/*
 * Instructions to Students:
 *
 * STEP 1: Fill in the following struct with your identifying info.
 */
#if 0
/*
 * STEP 2: Read the following instructions carefully.
 */
You will provide your solution to the Data Lab by
editing the collection of functions in this source file.
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.
  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;
  }
#endif
/*
 * STEP 3: Modify the following functions according the coding rules.
 *
 */
/* NO:1
 * bitAnd - x&y using only ~ and |
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
int bitAnd(int x, int y) {
  return ~((~x)|(~y));
}
/* NO:2
 * bitNor - ~(x|y) using only ~ and &
 *   Example: bitNor(0x6, 0x5) = 0xFFFFFFF8
 *   Legal ops: ~ &
 *   Max ops: 8
 *   Rating: 1
 */
int bitNor(int x, int y) {
  return (~x)&(~y);
}
/* NO:3
 * copyLSB - set all bits of result to least significant bit of x
 *   Example: copyLSB(5) = 0xFFFFFFFF, copyLSB(6) = 0x00000000
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int copyLSB(int x) {
  return (~0)+!(x&1);
  #if 0
    [Solution of Problem 3]
    Using ~0 we can get an 0xFFFFFFFF;
    If the LSB, which is the bit deciding whether the number is even or not, is equal to 1,
       the number should be odd and the answer should be 0xFFFFFFFF(~0),
       so we add nothing.
    If the LSB is equal to 0,
        the number should be even and the correct answer should be 0,
        which is equal to 0xFFFFFFFF+1,
        so we can add 1 to 0xFFFFFFF.
    In order to figure out whether the number is odd or even,
        we can use x%2, or x&1.
    So the correct answer should be (~0)+!(x&1);
    4 ops used.
  #endif
}
/* NO:4
 * evenBits - return word with all even-numbered bits set to 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 2
 */
int evenBits(void) {
  int p=0x55;
  p=(p<<8)+p;
  p=(p<<16)+p;
  return p;
  #if 0
    [Solution of Problem 4]
    I think this solution is not so concise, but I do not have any better ideas.
    Firstly,  we manually set a int p as 0x55(01010101 in binary).
    Then, we double it by (p<<8)+p and get 0xAAAA.
    Finally, we double it again and get 0x55, which is the answer we want.
    4 ops used.
  #endif
}
/* NO:5
 * logicalShift - shift x to the right by n, using a logical shift
 *   Can assume that 1 <= n <= 31
 *   Examples: logicalShift(0x87654321,4) = 0x08765432
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int logicalShift(int x, int n) {
  int p=(1<<31)&x;// Getting the sign bit.
  int u=x^p;//Changing the sign bit
  u>>=n;//Right move the u
  p=(!!p)<<30>>n<<1;//Add back the sign bit
  return u|p;
  #if 0
    [Solution of Problem 5]
    Firstly, we can simply get the sign bit by BitAnd x and 0x40000000(1<<31).
        After this operation, if the sign bit is 1, then set p to 0x40000000(1<<31).
        Otherwise, the p will be 0;
    Then, we can use u=x^p to get a number which is equal to p but the sign bit is set to 0.
    Then, when shr u, the logical shifting result will be equal to the result of arithmetic one.
    Finally we try to add the deleted sign bit back to new u. We need to shr the p logically, too.
        This process is same as shl 1 by (31-n). However, the '-' is forbidden.
        So we can <<31 and >>n. But it will reset the sign bit causing arithmetic shr.
        So we divide this process into 3 parts: <<30, >>n, and <<1 finally. And get an acceptable answer.
    10 ops used.
  #endif
}
/* NO:6
 * 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|(x>>16);
  x=x|(x>>8);
  x=x|(x>>4);
  x=x|(x>>2);
  x=x|(x>>1);
  return (~x)&1;
  #if 0
    [Solution of Problem 6]
    Maybe the simple thought is to find whether a bit is non-zero bit by bit.
    Because of limited operations, like problem 4, we need to cut the length of x half and half to reduce the steps.
    So we using shr to cut half and | it with the original one. As the result, the last bit will be the OR result of all bits.
    We can use &1 to get the last bit.
    12 ops used.
  #endif
}
/* NO:7
 * leastBitPos - return a mask that marks the position of the
 *               least significant 1 bit. If x == 0, return 0
 *   Example: leastBitPos(96) = 0x20
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 4
 */
int leastBitPos(int x) {
  return x&(~x+1);
   #if 0
    [Solution of Problem 7]
    There is a feature about "plus one" in binary.
        When we add 1 to a number in binary, we always reset all the non-zero bits after the first zero bit from the right.
        Then we change that zero bit into one.
    And this problem is similar. We need to change the first non-zero bit like the following process:
        x=[XXXX100...00] -> y=[XXXX000...00] -> ans=x^y=[0000100...00]
               ^                   ^                         ^
    The most difficult part is how to get the y above. What we need is to borrow the "plus-one" process to finish this task.
        We can using ~x as a springboard.
        x=[XXXX100...00] -> ~x=[PPPP011...11] -> ~x+1=[PPPP100...00]
               ^                    ^                      ^
        It may hard to get y when we found we are here. But you may observe (~x+1)&x is the correct answer.
    That is all.
    4 ops used.
  #endif
}
/* NO:8
 * TMax - return maximum two's complement integer
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 4
 *   Rating: 1
 */
int tmax(void) {
  return ~(1<<31);
  #if 0
    [Solution of Problem 8]
    That is 01111..11, which is equal to ~10000..0;
    2 ops used.
  #endif
}
/* NO:9
 * negate - return -x
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
  return (~x)+1;
  #if 0
    [Solution of Problem 9]
    As we know, -p=~p+1;
    For example, -1=[111...11]=[111...10]+1=~[000..01]+1=~(1)+1;
                  1=[000...01]=[000...00]+1=~[111..11]+1=~(-1)+1;
    //BTW: [...] means in binary.
    2 ops used.
  #endif
}
/* NO:10
 * 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)&(!(x>>31));
  #if 0
    [Solution of Problem 10]
    Firstly, get the sign bit as problem 5;
    Then using ! twice to change 0x40000000 to 1 while 0 to 0.
    However, we need to check the an special situation that x==0.
        0 is not positive, we need to & the answer given above with !!x to fix this.
    6 ops used.
  #endif
}
/* NO:11
 * isNonNegative - return 1 if x >= 0, return 0 otherwise
 *   Example: isNonNegative(-1) = 0.  isNonNegative(0) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 3
 */
int isNonNegative(int x) {
  return !(x>>31);
  #if 0
    [Solution of Problem 11]
    The same as the previous problem but we do not need to care about 0.
    4 ops used.
  #endif
}
/* NO:12
 * sum3 - x+y+z using only a single '+'
 *   Example: sum3(3, 4, 5) = 12
 *   Legal ops: ! ~ & ^ | << >>
 *   Max ops: 16
 *   Rating: 3
 */
/* A helper routine to perform the addition.  Don't change this code */
static int sum(int x, int y) {
  return x+y;
}
int sum3(int x, int y, int z) {
  int word1 = 0;
  int word2 = 0;
  /**************************************************************
     Fill in code below that computes values for word1 and word2
     without using any '+' operations
  ***************************************************************/
  word1=x^y^z;
  word2=((x&y)|(y&z)|(z&x))<<1;
  #if 0
    [Solution of Problem 12]
    I have read the book of Digital Logic about the adder. When we need to get c=a+b, we need to:
        1.Half add: It is the same as c[i]+=a[i]^b[i];
        2.Carry: if a[i]==b[i]==1, then c[i+1]++;
    We always repeatly accomplish this two processes bit by bit, because the c[i] may be changed when carrying.
    This problem provide us an opportunity to add. We may separate the two procedures into word1&2, then we can get the plus answer like:
        word1=x^y;
        word2=(x&y)<<1

    When there is 3 numbers needed to be added, it become confusing.
    We should redefine the half-adder and the carrier. A table may help:
        x[i]+y[i]+z[i]  word1[i]  word2[i+1]  TruthNumber
              00            0        0          0
              01            1        0          1
              10            0        1          2
              11            1        1          3
    We know a fact that when we XOR some bool values, the answer is whether the number of truth in them is odd or even.
        So the word1 should be x^y^z;
    So word2? If more than 2 truths exist, the result should be 1. So we can enumerate all the situation and get:
         word2=((x&y)|(y&z)|(z&x))<<1;
    That is all.
    8 ops used.
  #endif
  /**************************************************************
     Don't change anything below here
  ***************************************************************/
  return sum(word1,word2);
}
/* NO:13
 * addOK - Determine if can compute x+y without overflow
 *   Example: addOK(0x80000000,0x80000000) = 0,
 *            addOK(0x80000000,0x70000000) = 1,
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3
 */
int addOK(int x, int y) {
    int  p1=!(x>>31),p2=!(y>>31),p3=!((x+y)>>31);
    return (p1^p2) | (!(p1^p3));
    #if 0
        [Solution of Problem 13]
        Firstly, get the sign digits of x,y,x+y as p1, p2 and p3.
        Then check p1, p2, p3 by:
            if p1 != p2, then return 1;
            else if p1,p2!=p3 return 0;
            return 1;
        We can use xor to accomplish the same effect, that is return (p1^p2) | (!(p1^p3));
        11 ops used.
    #endif
}
/* NO:14
 * abs - absolute value of x (except returns TMin for TMin)
 *   Example: abs(-1) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 10
 *   Rating: 4
 */
int abs(int x) {
  int condition=!(x>>31);
  int value_when_true=x;
  int value_when_false=(~x)+1;
  int mask=condition+(~0);
  return (mask&value_when_true) | ((~mask)&value_when_false);
  #if 0
    [Solution of Problem 14]
    Amazingly, we find it intersting that we can accomplish a 'myIf' using 5 ops by:
        int myIf(int condition=...,/*0 or 1*/,int value_when_true=...,int value_when_false...)
        {
            int mask=condition+(~0);
            return (mask&value_when_true) | (~mask&value_when_false);
            /*
                This uses a feature that bin[11111..11]+1=bin[000000..0].
                And we know[11111..11]&x=x while [00000..00]&x=0.
            */
        }
    Using this "myIf" we all know this question is as same as:
        myIf(x>0,x,-x);
    We can simply use the solution of the previous problems to calculate x>0 and -x.
    10 ops used.
   #endif
}
/* NO:15
 * isNonZero - Check whether x is nonzero using
 *              the legal operators except !
 *   Examples: isNonZero(3) = 1, isNonZero(0) = 0
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 10
 *   Rating: 4
 */
int isNonZero(int x) {
    int n=~x+1;
    return ((n>>31)|(x>>31))&1;
  #if 0
    [Solution of Problem 15]
    I do not have any thoughts for a long time so I search the answer online.
    As we know, only when x=0 can we get x==-x. When talking about "Two's complement", only when x=0 can we get the last bit of x is equal to -x.
    So we can get the solution.
    6 ops used.
   #endif
}
int main(){
    int a,n;
    int i,Case;
    int result = 0;
    srand((unsigned)time(NULL));
    //function NO:1
    result=0;
    Case=1;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(bitAnd(x , y) != test_bitAnd(x,y))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",bitAnd(x , y), test_bitAnd(x,y));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO2
    result=0;
    Case=2;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(bitNor(x , y) != test_bitNor(x,y))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",bitAnd(x , y), test_bitAnd(x,y));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO3
    result=0;
    Case=3;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(copyLSB(x) != test_copyLSB(x))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",copyLSB(x), test_copyLSB(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO4
    result=0;
    Case=4;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(evenBits() != test_evenBits())
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",evenBits(), test_evenBits());
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO5
    result=0;
    Case=5;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()%31;
        if(logicalShift(x , y) != test_logicalShift(x,y))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",logicalShift(x , y), test_logicalShift(x,y));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO6
    result=0;
    Case=6;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(bang(x) != test_bang(x))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",bang(x), test_bang(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO7
    result=0;
    Case=7;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(leastBitPos(x) != test_leastBitPos(x))
        {
            result = 1;
            printf("[Wrong! with %d got %d != %d]\n",x,leastBitPos(x ), test_leastBitPos(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO8
    result=0;
    Case=8;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if( tmax() != test_tmax())
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",tmax(), test_tmax());
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO9
    result=0;
    Case=9;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(negate(x ) != test_negate(x))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",negate(x ), test_negate(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO10
    result=0;
    Case=10;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(isPositive(x) != test_isPositive(x))
        {
            result = 1;
            printf("[Wrong! with %d got %d != %d]\n",x,isPositive(x), test_isPositive(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO11
    result=0;
    Case=11;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        if(isNonNegative(x ) != test_isNonNegative(x))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",isNonNegative(x), test_isNonNegative(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO12
    result=0;
    Case=12;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        int z = rand()-RAND_MAX/2;
        if(sum3(x , y,z) != test_sum3(x,y,z))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",sum3(x , y,z), test_sum3(x,y,z));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO13
    result=0;
    Case=13;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(addOK(x , y) != test_addOK(x,y))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",addOK(x , y), test_addOK(x,y));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO14
    result=0;
    Case=14;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(abs(x ) != test_abs(x))
        {
            result = 1;
            printf("[Wrong! with %d != %d]\n",abs(x ), test_abs(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    //function: NO15
    result=0;
    Case=15;
    for(i = 0; i < 1000000; i++)
    {
        int x = rand()-RAND_MAX/2;
        int y = rand()-RAND_MAX/2;
        if(isNonZero(x ) !=  test_isNonZero(x))
        {
            result = 1;
            printf("[Wrong! with %d got %d != %d]\n",x,isNonZero(x ), test_isNonZero(x));
            break;
        }
    }
    if(result != 0)
        printf("NO.%d. Result is wrong with test case:%d\n",Case,result);
    else
        printf("NO.%d. All Passed!\n",Case);
    return 0;
}
Tests.h代码已折叠
展开折叠内容
/* Testing Code */
#include <limits.h>
int test_bitAnd(int x, int y)
{
  return x&y;
}
int test_bitNor(int x, int y)
{
  return ~(x|y);
}
int test_copyLSB(int x)
{
  return (x & 0x1) ? -1 : 0;
}
int test_evenBits(void) {
  int result = 0;
  int i;
  for (i = 0; i < 32; i+=2)
    result |= 1<<i;
  return result;
}
int test_logicalShift(int x, int n) {
  unsigned u = (unsigned) x;
  unsigned shifted = u >> n;
  return (int) shifted;
}



int test_bang(int x)
{
  return !x;
}
int test_leastBitPos(int x) {
  int mask = 1;
  if (x == 0)
    return 0;
  while (!(mask & x)) {
    mask = mask << 1;
  }
  return mask;
}
int test_tmax(void) {
  return LONG_MAX;
}
int test_negate(int x) {
  return -x;
}
int test_isPositive(int x) {
  return x > 0;
}
int test_isNonNegative(int x) {
  return x >= 0;
}
int test_sum3(int x, int y, int z)
{
  return x+y+z;
}
int test_addOK(int x, int y)
{
  int sum = x+y;
  return !(x < 0 && y < 0 && sum >= 0) && !(x > 0 && y > 0 && sum <= 0);
}
int test_abs(int x) {
  return (x < 0) ? -x : x;
}
int test_isNonZero(int x)
{
  return x!=0;
}

参考资料和拓展阅读

  1. http://www.cs.northwestern.edu/~wms128/bits.c

著作权声明[编辑]

关于[编辑]