计算机系统基础 实验一 csapp date lab1

知识储备
首先需要明白基本的一些操作符所代表的含义

按位与(&)、按位或(|)、按位异或(^)、按位加(+)、逻辑取反(!)、按位取反(~)、左移(<<)、右移(>>)

按位与运算将两个运算分量的对应位按位遵照以下规则进行计算:

  0 & 0 = 0, 0 & 1 = 0, 1 & 0 = 0, 1 & 1 = 1

按位或运算将两个运算分量的对应位按位遵照以下规则进行计算:

   0 | 0 = 0, 0 | 1 = 1, 1 | 0 = 1, 1 | 1 = 1

按位异或运算将两个运算分量的对应位按位遵照以下规则进行计算:

   0 ^ 0 = 0, 0 ^1 = 1, 1 ^ 0 = 1, 1 ^ 1 = 0

按位加运算将两个运算分量的对应为按位遵照以下规则计算:

   0 + 0 = 00 + 1 = 11 + 0 = 11 + 1 = 0

左移运算将一个位串信息向左移指定的位,右端空出的位用0补充,每左移1位相当于乘2

右移运算将一个位串信息向右移指定的位,右端移出的位的信息被丢弃,每右移1位,相当于除以2。对无符号数据,右移时,左端空出的位用0补充。对于带符号的数据,如果移位前符号位为0(正数),则左端也是用0补充;如果移位前符号位为1(负数),则左端用0或用1补充,取决于计算机系统。对于负数右移,称用0补充的系统为“逻辑右移”,用1补充的系统为“算术右移”。

按位取反为二进制串对应的1和0进行变换

逻辑取反只有0和非0两种情况

1.最低一位有效位置0
/* 
 *   lsbZero - set 0 to the least significant bit of x 
 *   Example: lsbZero(0x87654321) = 0x87654320
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 1
 */
int lsbZero(int x) {
  //x右移一位再左移一位实现把最低一位有效位置0
  x=x>>1;
  x=x<<1;
  return x;
}
2.x第n个字节实现取反
/* 
 * byteNot - bit-inversion to byte n from word x  
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByteNot(0x12345678,1) = 0x1234A978
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2
 */
int byteNot(int x, int n) {
  int y=0xff;
  n=n<<3;
  y=y<<n;
  x=x^y;
  return x;
}

补充说明一道题:x怎么取第n个字节

/* 
 * 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  (x>>8*n)&0xff
 */
int getByte(int x, int n) {

  return ((x>>(n<<3))&0xff);

}

题意解析:
对于0x12345678
当n=0时,return 0x78
当n=1时,return 0x56
当n=2时,return 0x34
当n=3时,return 0x12
解答过程与思考:
首先,在这个过程中,x应先右移8* n位再取最低的8位数值,
即 return (x>>8* n)&0xff
但是 不能用* 所以采用移位
return ((x>>(n<<3))&0xff);

3.比较x和y的第n个字节,如果相同,返回0,如果不相同,返回1
/* 
 *   byteXor - compare the nth byte of x and y, 
 *         if it is same, return 0, if not, return 1
 *   example: byteXor(0x12345678, 0x87654321, 1) = 1
 *			  byteXor(0x12345678, 0x87344321, 2) = 0
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 2 
 */
int byteXor(int x, int y, int n) {
  n=n<<3;
  x=(x>>n)&0xff;
  y=(y>>n)&0xff;
  return !!(x^y);
}
4.逻辑与

&&表示当两个操作数都是ture的时候,才返回true

/* 
 *   logicalAnd - x && y
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3 
 */
int logicalAnd(int x, int y) {
   x=!((!x)|(!y));
   //x=(!(!x)&(!(!y);
   return x;
}

设计思想:
1.把x和y分别取NOT,二者相或后再取NOT,即可得到逻辑与。
2.把x和y分别转换为逻辑的0和1,再相与。

5.逻辑或

|| 表示当两个操作数有一个为ture的时候,就返回true

/* 
 *   logicalOr - x || y
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3 
 */
int logicalOr(int x, int y) {
  x=(!(!x))|(!(!y));
  return x;
}

把x和y分别转换为逻辑的0和1,再相或。

补充一道题:逻辑右移位(默认为算术右移位)

 * 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) {
  int mask=~(((1<<31)>>n)<<1);
  return (x>>n)&mask;
}

若x为正数或0,
0x0xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
return x>>n即可。
若x为负数,1xxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
位运算时带符号整数 符号拓展前面将会补充1, 例如:
取(0x-87654321,4)
1000 7 6 5 4 3 2 1 右移4位,则1111 1000 7 6 5 4 3 2,后32-n位没问题
只需添加一个掩码,掩码把符号位转为0,若右移n位,则前n位应为0

1的原码为0000 0000 0000 0000 0000 0000 0000 0001
1<<31变成 1000 0000 0000 0000 0000 0000 0000 0000
右移(n-1)位,掩码为1111 0000 0000 0000 0000 0000 0000 0000
取反为 0000 1111 1111 1111 1111 1111 1111 1111
和x>>n进行与得到结果

由于可允许的运算符号 *Legal ops: ! ~ & ^ | + << >>*没有减号,所以将函数写成:

int logicalShift(int x, int n) {
    int mask=~(((1<<31)>>n)<<1);
    return (x>>n)&mask;
}
6.把x的前n位移到末尾
/* 
 * rotateLeft - Rotate x to the left by n
 *   Can assume that 0 <= n <= 31
 *   Examples: rotateLeft(0x87654321,4) = 0x76543218
 *   Legal ops: ~ & ^ | + << >> !
 *   Max ops: 25
 *   Rating: 3 
 */
int rotateLeft(int x, int n) {
    int y;
    y=~((~0)<<n);
    x=(x<<n)+((x>>(32+(~n+1)))&y);
    return x;
}

设计思想:先构造y为高(32-n)位为0的y,再与x右移(32-n)的x相与,相当于储存了x的高n位数,最后再与x左移n位相加即可。

方法一 参照上一道补充题,右移32-n位,将函数写成

int rotateLeft(int x, int n) {
	int mask=~(((1<<31)>>31)<<n);
    return (((x>>32)<<n)&mask)+(x<<n);
}

报错,right shift count >= width of type,x>>32时右移计数>=类型的宽度
修改,x>>(32-n),取n的补码,等于源码取反加一
x=(x<>(32+(~n+1)))&mask)即可
方法二 想构造掩码为1111 1111 1111 1111 1111 1111 1111 0000,还可让 y=~(( ~0)< logicalShift 函数也可以写成

int logicalShift(int x, int n) {
  int mask=~((~0)<<(32+(~n+1)));
  return (x>>n)&mask; 
}
7.如果x的二进制包含奇数个1,则返回1
/*
 * parityCheck - returns 1 if x contains an odd number of 1's
 *   Examples: parityCheck(5) = 0, parityCheck(7) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 4
 */
int parityCheck(int x) {
    x=x^(x<<16);
    x=x^(x<<8);
    x=x^(x<<4);
    x=x^(x<<2);
    x=x^(x<<1);
    x=x>>31;
  return !(!x);
}

设计思想:将x的前16位与后16位进行按位异或……
每次移位将x的低半位数与高半位数进行异或,实现得到的x位表示总是减去偶数个1,并不影响x位表示中1个数的奇偶性。最后把得到的x右移31位,并变成逻辑1和0.

8.确定是否可以在不溢出的情况下计算2*x
/*
 * mul2OK - Determine if can compute 2*x without overflow
 *   Examples: mul2OK(0x30000000) = 1
 *             mul2OK(0x40000000) = 0
 *         
 *   Legal ops: ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 2
 */
int mul2OK(int x) {
  return (((x>>31)&0x1)^((x>>30)&0x1))^0x1;
}

设计思想:计算2*x相当于逻辑左移一位,若x的符号位与第30位不同,则移位时符号位改变,溢出。
若x的符号位为1,则第30位为0时会溢出;若x符号位为0,则第30位为1时会溢出;于是将x的31位和30位分别与1相与,与1相与是本身,再异或,若相同异或为0,此时不溢出,由题意,再与1异或,使返回结果为1.

9.实现x*3/2向0舍入
/*
 * mult3div2 - multiplies by 3/2 rounding toward 0,
 *   Should exactly duplicate effect of C expression (x*3/2),
 *   including overflow behavior.
 *   Examples: mult3div2(11) = 16
 *             mult3div2(-9) = -13
 *             mult3div2(1073741824) = -536870912(overflow)
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int mult3div2(int x) {
    int t = (x << 1) + x; //t = 3*x
    int LSB = t & 0x1; //取出最低位
    int sign = (t >> 31) & 0x1; //取出符号位
    int ans = (t >> 1); //ans = 3*x/2;
    ans += (LSB & sign); //rounding toward 0;
    return ans;
}

设计思想:左移一位为x* 2,再+x即x* 3。

10.确定是否可以在不溢出的情况下计算x-y
/* 
 * subOK - Determine if can compute x-y without overflow
 *   Example: subOK(0x80000000,0x80000000) = 1,
 *            subOK(0x80000000,0x70000000) = 0, 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3
 */
int subOK(int x, int y) {
    int z;
    z=x+(~y+1);
    z=(z>>31)&0x1;
    x=(x>>31)&0x1;
    y=(y>>31)&0x1;
    return ((x^y)&(x^z))^0x1;
}

设计思想:x符号位与y符号位不同,且x符号位与(x-y)符号位也不同时,即溢出。

写法二:

int subOK(int x, int y) {
    int ans = x + (~y + 1); //ans = x - y;
    return !(((ans ^ x) & (x ^ y)) >> 31);
   //当且仅当两个加数的符号位相同,而与他们的和的符号位不同,发生溢出
}
11.求x的绝对值
/* 
 * absVal - absolute value of x
 *   Example: absVal(-1) = 1.
 *   You may assume -TMax <= x <= TMax
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 10
 *   Rating: 4
 */
int absVal(int x) {
    int y=x>>31;
    x=(y&(~x+1))+((~y)&x);
    return x;
}

设计思想:y = x>>31为x的符号位,当x符号位为0时,x绝对值就是x;当x符号位为1时,x绝对值是(~x+1)。

12.返回与f的绝对值相等的位级别
/* 
 * float_abs - Return bit-level equivalent of absolute value of 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 float_abs(unsigned uf) {
    int x=uf&0x7fffffff;
    if(x>0x7f800000)
        return uf;//NaN
    else
        return x;
}

设计思想
NaN(Not a Number,非数)表示未定义或不可表示的值。常在浮点数运算中使用。NaN 属性代表一个“不是数字”的值。这个特殊的值是因为运算不能执行而导致的,不能执行的原因要么是因为其中的运算对象之一非数字(例如, “abc” / 4),要么是因为运算的结果非数字(例如,除数为零)。
NaN表示的数阶码全为1,且小数域为非0。1111 1111 **** **** **** **** **** ***

解答过程与思考:
如果uf是NaN 返回NaN 否则,返回f而且可以用if语句
0 1111 1111 0000 0000 0000 0000 0000 000
只要后面32位中有一位不是0就是NaN
首先判断 是否是NaN
将uf的符号位变为0,得到的数若比0x7f80000大,即为NaN。

13.返回unsigned uf的整型数的二进制形式
/* 
 * float_f2i - 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
 * 返回unsigned uf的整型数的二进制形式
 * 参数和结果都会被作为unsigned返回,但是会表示为二进制的单精度浮点值
 *   任何超过范围的数都应该返回 0x80000000u.
 *   允许的操作符: 任何整数或者无符号数操作符包括: ||, &&. also if, while
 *   最多操作符数目: 30
 *   分值: 4
 */
int float_f2i(unsigned uf) {
    int x,y;
    unsigned mini=0x80000000;
    x=(uf>>23)&0xff;//阶码域
    y=(uf&0x007fffff)^0x00800000;
    if(x>158)
            return mini;
    if(x<127)
            return 0;
    else if(((uf>>31)&0x1)==1)
    {
        if(x>150)
            return ((~(y<<(x-150)))+1);
        else
            return ((~(y>>(150-x)))+1);
    }
    else
    {
        if(x>150)
            return (y<<(x-150));
        else
            return (y>>(150-x));
    }
}

设计思想:将uf左移23位并与0xff相与来取出阶码域;设置y来取出小数域,并令其第23位为1,为了规格化的值隐含的以1开头的表示。

当阶码的值E大于31时,表示float转化为int会溢出,或者出现阶码域全为1的情况;当阶码的值小于0,返回0;当uf最高位为1时,判断x与150的大小,决定移位的方向,并以补码形式将数值存储;当uf最高位为0时,判断x与150大小,决定移位的方向。

你可能感兴趣的:(计算机系统基础)