文章目录
- 前言
- 一、bitXor(异或)
- 二、tmin(最小的二进制补码)
- 三、isTmax(判断是否为最大值)
- 四、allOddBits(判断奇数位是否都是1)
- 五、negate(计算相反数)
- 六、isAsciiDigit(判断0x30 <= x <= 0x39)
- 七、conditional(实现x ? y : z)
- 八、isLessOrEqual(比较两个数的大小)
- 九、logicalNeg(逻辑取反)
- 十、howManyBits(计算位数)
- 十一、floatScale2(将单精度浮点数乘以 2)
- 十二、floatFloat2Int(将单精度浮点数转为整数)
- 十三、floatPower2(计算2.0ˆx)
- 总结
前言
一个本硕双非的小菜鸡,备战24年秋招。刚刚看完CSAPP,真是一本神书啊!遂尝试将它的Lab实现,并记录期间心酸历程。
官方网站:CSAPP官方网站
以下是官方文档翻译:
此分配的目的是为了更熟悉整数和浮点数的位级表示。你可以通过解决一系列的编程“谜题”来做到这一点。这些谜题中有很多都是人为的,但你会发现自己在思考一些细节。
bits.c文件包含了13个编程谜题中的每一个的骨架。你的任务是只使用整数谜题(即没有循环或条件)和有限数量的C运算和逻辑运算符来完成每个函数骨架。具体来说,您只允许使用以下八个运算符:
! ˜ & ˆ | + << >>
其中一些函数进一步限制了这个列表。此外,您不允许使用任何超过8位的常量。有关详细的规则和关于所需的编码风格的讨论,请参见bits.c中的注释。
本节描述您将以位解决的谜题。
表1列出了从最简单到最难的按难度顺序排列的谜题。“评级”字段给出了谜题的难度评级(点数),而“最大ops”字段给出了您允许用于实现每个函数的最大操作符数。有关函数所需行为的更多细节,请参见bits.c中的注释。您也可以参考test.c中的测试功能。这些功能被用作引用函数来表示函数的正确行为,尽管它们不满足函数的编码规则。
对于浮点难题,您将实现一些常见的单精度浮点运算。对于这些谜题,您被允许使用标准控制结构(条件、循环),并且您可以同时使用int和无符号数据类型,包括任意无符号和整数常量。您不能使用任何联合、结构或数组。最重要的是,您可能不使用任何浮点数据类型、操作或常量。相反,任何浮点操作数都将作为具有类型的形式传递给函数无符号,任何返回的浮点值都将为无符号类型。代码应该执行实现指定浮点操作的位操作。
每个"Expr"是一个表达式,只使用以下内容:
- 整数常量0到255 (OxFF),包括不允许使用像oxffffffff这样的大常量。
- 函数参数和局部变量(没有全局变量)。
3.一元整数运算!~ - 二进制整数运算& ^ / + << >>
有些问题甚至进一步限制了允许的操作符集合。每个“Expr”可以由多个操作符组成。
您不局限于每行一个操作符。明确禁止您:
- 使用任何控制结构,如if、do、while、for、switch等。
- 定义或使用任意宏。
3.在此文件中定义任何附加函数。 - 调用任意函数。
- 可使用“&&”、“11”、“-”、“?”等其他操作:
- 使用任何形式的铸造。
- 使用除int以外的任何数据类型。
这意味着不能使用数组、结构体或联合。
您可以假设您的机器:
- 使用2s补码,32位整数表示。
- 按算术方式执行右移。
3.如果移位量小于0或大于31,则在移位时具有不可预测的行为。
浮点编码规则
对于需要实现浮点运算的问题,
编码规则没有那么严格。你可以使用循环和
有条件的控制。你可以同时使用整型和无符号。
可以使用任意整数和无符号常数。你可以用任何算术,
对int或unsigned数据的逻辑操作或比较操作。
你被明确禁止:
- 定义或使用任何宏。
- 在此文件中定义任何其他函数。
3.调用任意函数。 - 使用任何形式的铸造。
- 使用除int或unsigned之外的任何数据类型。这意味着你
不能使用数组、结构或联合。 - 使用任何浮点数据类型、操作或常量。
注:
- 使用dlc(数据实验室检查器)编译器(在讲义中描述)检查你的解决方案的合法性。
- 每个函数都有一个最大的操作数(整数、逻辑、或者比较),你可以在你的实现中使用函数的。最大运算符计数由dlc检查。注意,赋值(‘=’)不计算;你可以用尽可能多的这些都是你想要的,没有惩罚。
3.使用最好的测试工具来检查函数的正确性。 - 使用BDD检查器来正式验证您的函数
- 函数中给出了每个函数的最大操作数
每个函数的头注释。如果有任何不一致在写入操作和此文件中的最大操作之间,考虑这个文件是权威的来源。
我注:瞅代码介绍就行,要会编译。
一、bitXor(异或)
/ *
- bitXor—x^y,只使用~和&
*示例:bitXor(4,5) = 1
*合法操作:~ &
*最大ops: 14
*评分:1 - /
第一眼瞅到这道题我愣了两秒,在我心中CSAPP不得极为高大上且难度巨高。。。然而虽然是第一题,但是我认为也没啥难度(插旗中等到时候回来被打脸)。然后就没过去,其实主要是文档的锅,我看了个||瞅差了。回头发现是异或。
异或就是异为1同为0,这道题要是想出现比较结果只能依靠&。
首先第一想法就是依靠&把相同排出去,写了个 ~(x & y),这个意思是不是同1为1,然后我就想到了 ~( ~x & ~y) ,这是不是同0为1,他俩的&就是答案,这就解决啦!
//1
/*
* 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) & ~(x & y);
}
Score Rating Errors Function
1 1 0 bitXor
Total points: 1/1
二、tmin(最小的二进制补码)
/ *
- tmin—返回最小2的补数整数
*法律操作:!n .;在比;
*最大ops: 4
*评分:1 - /
又是翻译的锅,第一眼没看懂题啥意思,后来看解释明白了(所以英语一定要学好啊)。
当时看CSAPP的时候就研究过,当码长为8时,0的补码为0000 0000,则0-1也就是-1的补码就是1111 1111,所以最小为-128即1000 0000。至于为啥不再减一,因为再减一0111 1111就转正为127了。类比此题就是把1左移31位(因为该代码运行在32位系统上)
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 1 << 31;
}
Score Rating Errors Function
1 1 0 tmin
Total points: 1/1
三、isTmax(判断是否为最大值)
/ *
- isTmax——如果x是最大值,则返回1,如果是2,则返回2。
*,否则为0
*法律操作:!n .
*最大ops: 10
*评分:1 - /
对我来说真的是一道很精彩的题目,想了一个小时才勉强搞定。主要是!与~的区别吧,还有0x80000000这个不好区分。总之真是一道很棒的题目啊!
思路最开始是想和0x7fffffff比较来着(犯了if语句用得太多的毛病),然后发现不对后改用+1判断,发现0x80000000这个相反数是跟本身一致的,然后又想到了第一题(当时还直接拷贝过来了,结果发现其实可以直接用^了)。然后就是经典的双数问题,改了又改还参考了一下其他人写的才弄好。
//2
/*
* 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) & !((~x) ^ (x + 1));
}
Score Rating Errors Function
1 1 0 bitXor
Total points: 1/1
四、allOddBits(判断奇数位是否都是1)
/ *
- allOddBits -如果单词中所有奇数位都为1,则返回1
*位从0(最低有效)到31(最高有效)
allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
*法律行动:!n; n;在比;
*最大ops: 12
*评分:2分 - /
有了上一道题的经验就知道这类题该怎么做了(但其实后来发现是两个类型的),上来先找规律做比较,然后“惊讶”的发现lab是可以用=的,只是不能超限。。。
想办法凑异或呗,既然可以这么搞那就拿0xAA做比较,先想办法弄出一个0xAAAAAAAA来,移位操作再加到一起,然后一个逻辑取反。。。
一顿操作猛如虎,一看没过。奥,二百五竟是我自己,偶数位不用考虑忘了,那就先控下数再比较。
/*
* 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 m1 = 0xAA;
int m2 = m1 + (m1 << 8);
int m3 = m2 + (m2 << 16);
return !(m3 ^ (x & m3));
}
Score Rating Errors Function
2 2 0 allOddBits
Total points: 2/2
五、negate(计算相反数)
/ *
*负-返回-x
*示例:negate(1) = -1。
*法律行动:!n; n;在比;
*最大操作数:5
*评分:2分
- /
送分题,送分题!当时我在第三题有多狼狈,逻辑取反和按位取反整的糊涂,现在我就有多嚣张(不是)。按位取反可以通俗理解为*(-1)再-1,所以这题就是先按位取反再+1。
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return ~x + 1;
}
Score Rating Errors Function
2 2 0 negate
Total points: 2/2
六、isAsciiDigit(判断0x30 <= x <= 0x39)
/ / 3
/ *
- isAsciiDigit—如果0x30 <= x <= 0x39(字符’0’到’9’的ASCII编码),则返回1
*示例:isAsciiDigit(0x35) = 1。 - isAsciiDigit(0x3a) = 0。
- isAsciiDigit(0x05) = 0。
*法律操作:!n .;在比;
*最大ops: 15
*评分:3 - /
思路好想,结果我写错了,忘了异或是按位操作的,忘加!转数字了,试了很多次才发现问题出在什么地方。
//3
/*
* 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 & 8) ^ 0) + !((x & 14) ^ 8)) & !((x >> 4) ^ 3);
}
Score Rating Errors Function
3 3 0 isAsciiDigit
Total points: 3/3
七、conditional(实现x ? y : z)
/ *
*有条件的——与x相同?Y: z
*示例:条件(2,4,5)= 4
*法律操作:!n .;在比;
*最大ops: 16
*评分:3
- /
这题做出来没费劲,解释费老大力了,因为我最后时候居然解释不通我的代码了。
大概路子是利用0111和0000的性质了。
主要我面临的问题是反补码和~的不清楚,这次通过这道题应该没问题了,熟悉了很多。这道题对我影响意义很大!
记住1的补码是-1,-1的补码是1111,0的补码还是0。
~0 = 1111
/*
* 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) {
int res = ~(!(x ^ 0)) + 1;
return (~res & y) | (res & z);
}
Score Rating Errors Function
3 3 0 conditional
Total points: 3/3
八、isLessOrEqual(比较两个数的大小)
/ *
- isLessOrEqual—如果x <= y,则返回1,否则返回0
*示例:isLessOrEqual(4,5) = 1。
*法律操作:!n .;在比;
*最大ops: 24
*评分:3 - /
心态爆炸的一道题,其实思路还是挺清晰的,但就是位操作与或非啥的太乱了,给我整的心态爆炸,费了三张演算纸,一个下午的时间才勉强解决。。。
通俗易懂的分类讨论,同号异号,正负号以及值相同的情况。我主要面对的问题是写着写着就乱了不知道是0是1(当时脑子也乱就觉得马上就能解决了),后来休息的时候发现这样不行,拿演算纸好好推导了一下思路,这才使得难度变简单了些。结果千辛万苦发现没有考虑值相等的情况,又是一顿捅咕。
说多了都是泪啊,切记!
/*
* isLessOrEqual - if x <= y then return 1, else return 0
* Example: isLessOrEqual(4,5) = 1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 24
* Rating: 3
*/
int isLessOrEqual(int x, int y) {
int flagX = (x >> 31) & 1;
int flagY = (y >> 31) & 1;
int z = y + (~x + 1);
int flagZ = !((z >> 31) & 1) | !(z ^ 0);
return (!(~flagX & flagY)) & ((flagX & ~flagY) | (flagZ));
}
Score Rating Errors Function
3 3 0 isLessOrEqual
Total points: 3/3
九、logicalNeg(逻辑取反)
/ / 4
/ *
- logicalNeg—实现!操作符,使用all of
*合法的操作符except !
*示例:logicalNeg(3) = 0, logicalNeg(0) = 1
*合法操作:~ & ^ | + <<在比;
*最大ops: 12
*评分:4 - /
柳暗花明又一村。这题好歹回了点信心,太惨了。
//4
/*
* 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) {
return ((x | (~x + 1)) >> 31) + 1;
}
Score Rating Errors Function
4 4 0 logicalNeg
Total points: 4/4
十、howManyBits(计算位数)
/* howManyBits:返回表示x所需的最小位数
*二进制补码
*例子:howManyBits(12) = 5
*多少位(298)= 10
*多少位(-5)= 4
*多少位(0)= 1
*多少位(-1)= 1
- howManyBits(0x80000000) = 32
*法律操作:!n .;在比;
*最大ops: 90
*评分:4 - /
思路不难,可惜当时没有坚持到最后,还是参考了别人的代码。
考虑正负,再每一位查找1。
/* 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 sign = x >> 31;
x = (sign & ~x) | (~sign & x);
int High_16 = (!(!(x >> 16))) << 4;
x = x >> High_16;
int High_8 = (!(!(x >> 8))) << 3;
x = x >> High_8;
int High_4 = (!(!(x >> 4))) << 2;
x = x >> High_4;
int High_2 = (!(!(x >> 2))) << 1;
x = x >> High_2;
int High_1 = (!(!(x >> 1)));
x = x >> High_1;
int High_0 = x;
return High_16 + High_8 + High_4 + High_2 + High_1 + High_0 + 1;
}
Score Rating Errors Function
4 4 0 howManyBits
Total points: 4/4
十一、floatScale2(将单精度浮点数乘以 2)
/ /浮动
/ *
- floatScale2——返回与表达式2*f for等价的位级别
*浮点参数f。
*参数和结果都以unsigned int类型传递,但是
*它们被解释为的位级表示
*单精度浮点值。
*当argument是NaN时,返回argument
*合法操作符:任何整数/无符号操作符,包括。||,&&。还有if, while
*最大ops: 30
*评分:4 - /
注意:浮点数解除了很多限制,比如可以使用if,while等函数了。
主要是考察浮点数的定义:浮点数表示由符号位,尾数位和阶码位,又有单精度和双精度之分。根据阶码位分为规格化数、非规格化数和特殊值。都要分别考虑。
//float
/*
* 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 s = uf & (1 << 31);
unsigned exp = (uf & 0x7f800000) >> 23;
unsigned frac = uf & (~0xff800000);
if (exp == 0) return frac << 1 | s;
if (exp == 255) return uf;
exp++;
if (exp == 255) return 0x7f800000 | s;
return s | (exp << 23) | frac;
}
Score Rating Errors Function
4 4 0 floatScale2
Total points: 4/4
十二、floatFloat2Int(将单精度浮点数转为整数)
/ /浮动
/ *
- floatScale2——返回与表达式2*f for等价的位级别
*浮点参数f。
*参数和结果都以unsigned int类型传递,但是
*它们被解释为的位级表示
*单精度浮点值。
*当argument是NaN时,返回argument
*合法操作符:任何整数/无符号操作符,包括。||,&&。还有if, while
*最大ops: 30
*评分:4 - /
分情况讨论的一题,根据阶码的值可分为>31(溢出),<0(非规格化数且接近0),>23(是一个没有小数点的23位数)以及>0的数,每一种都有各自的判断及结果标准。
ps:半天没通过,搞得我以为哪里判断错了,回头一瞅发现127打成172了。。。
/*
* 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 s = uf & (1 << 31);
unsigned exp = (uf & 0x7f800000) >> 23;
unsigned frac = (uf & 0x7fffff) | 0x800000;
int E = exp - 127;
if (exp == 255 || E > 31 || frac >> 31) return 0x80000000;
if (E < 0) return 0;
if (E > 23) frac <<= (E - 23);
else frac >>= (23 - E);
if (!((uf >> 31) ^ (frac >> 31))) return frac;
return ~frac + 1;
}
Score Rating Errors Function
4 4 0 floatFloat2Int
Total points: 4/4
十三、floatPower2(计算2.0ˆx)
/ *
- floatPower2—返回与表达式2.0^x等价的位级别
*(2.0的x次方)表示任意32位整数x。
*返回的unsigned值应该具有相同的位
*表示为单精度浮点数2.0^x。
*如果结果太小,无法用denorm表示,则返回
- 0。如果太大,返回+INF。
*合法操作符:任何整数/无符号操作符,包括。||,&&。还有if, while
*最大ops: 30
*评分:4
- /
这个相对来说真的简单,可能是老师最后的仁慈了(笑)。记住阶码的值的概念,然后讨论一下太大溢出和太小无法表示的情况就ok啦!
/*
* 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 res = x + 127;
if (res >= 255) return 0x7f800000;
if (res < 0) return 0;
return res << 23;
}
Score Rating Errors Function
4 4 0 floatPower2
Total points: 4/4
总结
一路回望,诸多不易。只能说CSAPP不愧是神书,其中的lab更是神中神,我感觉做完之后我能暴打一周前的我,感觉看完了会了跟动手实操完全是两回事,更何况这十三道题更是你没学精就是白扯的。强烈推荐有时间的朋友们一定要坚持到最后,你会感激当时坚持到最后的自己的,加油!