花门楼前见秋草,岂能贫贱相看老。这篇文章主要讲述CSAPP-datalab相关的知识,希望能为你提供帮助。
经典重温,冲冲冲。
Integer
bitXor
/*
* 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) &
~(~y &
x));
}
解题思路:布尔代数。对于异或,从定义式出发,并使用「德摩根定律」变换:
\\[\\begin{align} A \\oplus B & = \\overline{A}B + \\overline{B}A \\\\ & =\\overline{(\\overline{\\overline{A}B}) \\cdot (\\overline{\\overline{A}B})} \\end{align} \\]
tmin
/*
* tmin - return minimum two\'s complement integer
*Legal ops: ! ~ &
^ | + <
<
>
>
*Max ops: 4
*Rating: 1
*/
int tmin(void)
{
return 1 <
<
31;
}
解题思路:Nothing special .
isTmax
/*
* 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));
}
解题思路:
isTmax(x) = 1 iff x == 0x7fffffff
。那么 x+1 = 0x80000000
,显然 x ^ (x+1) == (int)(-1)
。ti需要考虑的特殊情况是 x = -1
,因此加入 !!(x + 1)
的情况。allOddBits
/*
* 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 mask = (0xAA <
<
8) | 0xAA;
mask = (mask <
<
16) | mask;
return !((x &
mask) ^ mask);
}
解题思路:偶数位上的
1
对本题毫无用处,因此构造掩码 mask = 0xAAAAAAAA
将偶数位全部清零,也就是 y = x &
mask
。题目要求奇数位上必须全为 1
,就说明 y == 0xAAAAAAAA == mask
,因此 (y ^ mask) == 0x0
。negate
/*
* negate - return -x
*Example: negate(1) = -1.
*Legal ops: ! ~ &
^ | + <
<
>
>
*Max ops: 5
*Rating: 2
*/
int negate(int x)
{
return (~x) + 1;
}
解题思路:补码的性质,相反数是「按位取反再加一」。
isAsciiDigit
/*
* 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 + (~0x30) + 1) >
>
31) &
1) ^ 1) &
((((0x39 + (~x) + 1) >
>
31) &
1) ^ 1);
}
解题思路:思路很明显,就是需要判断
0x30 <
= x &
&
x <
= 0x30
。下面来看如何判断 a <
= b
。a <
= b
->
b - a >
= 0
->
b + (~a) + 1 >
= 0
->
sign(b + ~a + 1) == 0
->
sign(b + ~a + 1) ^ 1 == 1
sign(x) = (x >
>
31) &
1
表示 x
的符号位。conditional
/*
* 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)
{
x = ~(!!x) + 1;
return (y &
x) + (z &
(~x));
}
解题思路:原本的想法是构造
y*x + z*(!x)
这种形式作为返回值,其中 x
为 1 或 0 。但是不能用乘法,所以重新思考了一下,构造 (y &
x) + (z &
~x)
,其中 x
为 0x0
或者 0xffffffff
。- 当
x == 0
时,需要构造x = 0x0 = -1 + 1 = ~0 + 1
。 - 当
x != 0
时,需要构造x = 0xffffffff = -1 = negate(1) = negate(!!x) = ~(!!x) + 1
。
!!x
可以将 int
映射为逻辑真值。isLessEqual
/*
* isLessOrEqual - if x <
= ythen return 1, else return 0
*Example: isLessOrEqual(4,5) = 1.
*Legal ops: ! ~ &
^ | + <
<
>
>
*Max ops: 24
*Rating: 3
*/
int isLessOrEqual(int x, int y)
{
int signx = (x >
>
31) &
1;
int signy = (y >
>
31) &
1;
int k1 = (signx ^ signy ^ 1) &
(((x + (~y)) >
>
31) &
1);
int k2 = (signx &
(signy ^ 1));
return k1 | k2;
}
解题思路:看「最大数值」这一小节 。
logicalNeg
/*
* 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)
{
int neg = (x >
>
31) &
1;
int plus = ((~x + 1) >
>
31) &
1;
return (neg ^ 1) &
(plus ^ 1);
}
解题思路:根据「0 既不是正数,也不是负数」这一个规律。所以如果
x
为 0 ,那么 x
和 -x
的符号位都必然是 0
。howManyBits
/* 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 b16, b8, b4, b2, b1;
x = (x >
>
31) ^ x;
b16 = (!!(x >
>
16)) <
<
4, x >
>
= b16;
b8 = (!!(x >
>
8)) <
<
3, x >
>
= b8;
b4 = (!!(x >
>
4)) <
<
2, x >
>
= b4;
b2 = (!!(x >
>
2)) <
<
1, x >
>
= b2;
b1 = (!!(x >
>
1)), x >
>
= b1;
return b16 + b8 + b4 + b2 + b1 + x + 1;
}
解题思路:有点类似二分查找,本题自己没做出来,是看了几个题解才写出来的。
Float floatScale2
/*
* 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 sign = (uf &
0x80000000);
unsigned exp = (uf &
0x7f800000);
unsigned frac = (uf &
0x7fffff);
//clear sign bit
uf = uf &
(0x7fffffff);
//special cases: INF, NaN, zero
if (exp == 0x7f800000 || uf == 0)
return uf | sign;
if (exp == 0) // uf is a denormalize number
{
if ((frac >
>
22) == 1) // 2*uf change into a normalized number
{
exp = (1 <
<
23);
frac = (frac <
<
1) &
0x7fffff;
return sign | exp | frac;
}
else // 2*uf is still a denormalized number
return sign | (frac <
<
1);
}
else // uf is a normalized number, so 2*uf must be a normalized number, too
{
exp += (1 <
<
23);
return sign | exp | frac;
}
}
解题思路:需要熟悉浮点数编码「IEEE 754」,然后是分类讨论。
uf
是非规格化数
2*uf
仍然是非规格化数2*uf
是规格化数
uf
是规格化数
【CSAPP-datalab】那么2*uf
必然是规格化数。
- 其他特殊情况
uf
是无穷大,NaN,0 。
/*
* 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 sign = (uf &
0x80000000);
unsigned exp = (uf >
>
23) &
0xff;
unsigned real = (uf &
0x7fffff) | (1 <
<
23);
unsigned t;
uf = uf &
0x7fffffff;
if (exp <
127) // uf <
1.0
return 0;
if (exp >
= 150) // we can keep all 23 frac
{
t = exp - 150;
if (t <
8) // round(uf) is in int range, and we can put no more than 7 zeros after real
{
real = real <
<
t;
return (sign == 0) ? real : (-real);
}
return 0x80000000;
// out of range, we can\'t put more than 7 zeros after real
}
real = real >
>
(150 - exp);
// the last serval bits of frac should be thrown
return (sign == 0) ? real : (-real);
}
本题其实仍然是分类讨论,但是分类的依据与某些特殊情况,需要不断测试才能写出代码,所以一定不能畏难,不会就马上找题解,这样收获会很小。上面的代码也是修改了好多次的,我也忘了最初的版本长什么样子了。解法也没什么特别的,就是不断地试,观察不通过的 Test Case ,然后修改。
首先有一个 trick 是把隐含的 1 还原的:
unsigned real = (uf &
0x7fffff) | (1 <
<
23);
/*
* Here is a sample:
*155.55 = 0 10000110 00110111000110011001101
*frac= 1.00110111000110011001101
*exp = 10000110 = 134, E = 7
*so the float val is:
*fval = 1.00110111000110011001101 × 2^7
*= 10011011.1000110011001101
*when fval cast into int, the \'1000110011001101\' will be thrown:
*ival = 0...0 10011011 = 155
*/
此时,我们默认小数点的位置在第 23 bit 与 第 22 bit 之间。
后面我们要解决的就是小数点移动到后面的哪一位的问题,这就要根据
E = exp - 127
的具体数值来决定:- 如果
E > = 23
,那么real
后面的所有小数位都得以保留,并且需要添加E - 23
个 0 。 - 如果
E < = 22
,那么说明real
的小数点最远就只能移动到第 1 bit 与第 0 bit 之间,小数点后的位都需要「丢弃」。
下面解析代码中的几个
if
条件。exp < 127
对于非规格化数,一定是小于 1.0 的,因为其值为 \\(denormalied = 0.frac \\times 2^{-126}\\) 。对于规格化数,\\(normalized = 1.frac \\times 2^{exp - 127}\\) ,要使uf < 1.0
,就要令 \\(exp - 127 \\le -1\\) ,所以可得uf < 1.0
的等价条件为exp < = 126
。
exp > = 150
此情况是可保留 23 位frac
。令E = exp - 127 > = 23
,即可得:exp > = 150
。超出 23 的部分就要补上exp - 150
个 0 ,但是最多只能补 7 个 0 ,因为real
本身就有 24 位,int
的符号位占 1 位。
/* * For an example: *999999999.0 = 0 10011100 11011100110101100101000 *frac =11011100110101100101000 *real = 1.11011100110101100101000 *exp = 10011100 = 0x9c = 156, E = 29 *so the float val is: *fval = 1.11011100110101100101000 × 2^(29) *= 111011100110101100101000 × 2^(6) *= 111011100110101100101000 000000 *when cast into int val: *ival = 111011100110101100101000 000000 *ival has 24 + 6 = 30 bits, put it into 32 bits is: *ival = (00 111011100110101100101000 000000)2 = 1000000000 *the number \'1000000000\' is decimal */
127 < = exp < = 149
这里是就是单纯的「舍弃」小数点后面的数值。看上面提到的例子:
/* * Here is a sample: *155.55 = 0 10000110 00110111000110011001101 *real= 1.00110111000110011001101 *exp = 10000110 = 134, E = 7 *so the float val is: *fval = 1.00110111000110011001101 × 2^7 *= 10011011.1000110011001101 *when fval cast into int, the \'1000110011001101\' will be thrown: *ival = 0...0 10011011 = 155 */
/*
* 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)
{
int exp = x + 127;
if (1 <
= exp &
&
exp <
= 254) // 2^x can be a normalized number
return exp <
<
23;
else if (exp >
= 255) // 2^x out of range
return 0x7f800000;
else // x <
= -127 (exp <
= 0), 2^x may be a denormalized number, maybe zero
{
if (x >
= -149)
return 1 <
<
(149 + x);
else
return 0;
}
}
本题要求实现 \\(2^x\\) 。显然,这就是浮点数的一种十分特殊的情况:\\(1.0...0 \\times 2^{exp - 127}\\) 。但是 \\(exp \\in [1,254]\\) ,这需要对
exp = E + 127 = x + 127
进行讨论。注意 exp
必须要使用有符号数进行存储,因为 x + 127
仍可能为负数 。1 < = exp & & exp < = 254
说明 \\(2^x\\) 在规格化数的范围内,即 \\(val = 1.0\\cdot\\cdot\\cdot0 \\times 2^x\\) ,所以frac = 0...0, exp = x + 127
。
exp > = 255
说明 \\(2^x\\) 超过了float
的有效范围。
exp < = 0
这时候 \\(2^x\\) 就必须采用非规格化数来存储。非规格化数 \\(denormalized = 0.frac \\times 2^{-126}\\) ,所以:
- \\(2^x\\) 的最大值为 \\(max = 0.10\\cdot\\cdot\\cdot0 \\times 2^{-126} = 2^{-127}, x=-127\\)
- \\(2^x\\) 的最小值为 \\(min = 0.0\\cdot\\cdot\\cdot01 \\times 2^{-126} = 2^{-149}, x=-149\\)
frac
中必定只有一个 1 ,而这个 1 的后面 0 的数目取决于 \\(x\\) 的大小,实际上不难发现是 \\(149 + x\\) 。
- \\(2^x\\) 的最大值为 \\(max = 0.10\\cdot\\cdot\\cdot0 \\times 2^{-126} = 2^{-127}, x=-127\\)
unix >
./driver.pl
Correctness ResultsPerf Results
PointsRatingErrorsPointsOpsPuzzle
11028bitXor
11021tmin
11028isTmax
22027allOddBits
22022negate
330213isAsciiDigit
33028conditional
330214isLessOrEqual
44029logicalNeg
440232howManyBits
440222floatScale2
440218floatFloat2Int
440210floatPower2Score = 62/62 [36/36 Corr + 26/26 Perf] (152 total operators)
SummaryEnjoy the fun of writing !
推荐阅读
- uni-app基础组件
- eclipse搭建安卓(Android)开发环境
- android中简单便捷使用GreenDao本地数据库及采坑之路
- call applybind 的区别,this的四种绑定方式
- happen-before原则的理解
- Android Studio学习路程(13)
- Project facet Cloud Foundry Standalone Application version 1.0 is not supported
- 修改Android Studio新建工程时repositories的默认配置
- Android系统研究资料收集---站在前人的肩膀上