先贴出啦本次实验代码,rating 1,2,3的部分就不写分析了,看简单的注释应该看的懂。
主要是部分rating4的。
先贴代码
/*
* CS:APP Data Lab
*
*
* 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 header; it confuses the dlc
* compiler. You can still use printf for debugging without including
* , 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;
}
Rating 4 的题目如bang,需要知道的是2进制补码的一些特征。对于任何一个x(除零外)x和-x中必然有一个数的符号位为1.所以直接比较x|-x的符号位即可。
int bang(int x) {
x = (~x+1) | x;
return (~(x >> 31)) & 0x1;
}
对于题目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也有着同样巧妙的地方。
对于一个int型的x取以2为底的对数。最大为31,最小为0。(因为题目要求x的取值为0x7fffffff~0x00000001)而这个题目的本质其实位最高位‘1‘在哪里
这两个特征允许我们用一个5位长的位串(定义为table)就可表示0到31的位就可以。如:10111(2)=23(10)这样我们就很好做题了
依旧是二分法:
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型的进位和舍弃的问题。
做这道题之前要清楚一个问题:float在计算机中是怎么表示的。
一个32位的float中 有一个符号位,8位表示阶码,23位尾数
那么一个int型的数如:0x1234转换位float需要四步
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,此时要进位)