CSAPP datalab
写在前面:出于 hornor code 要求,这些答案本不应出现在这里,但是考虑到这些 lab 已经成型了,不同于 CMU15-445 这种每年的 lab 都会变的变态课,csapp 的 lab 答案早已被解析透了,我这里权当个人记录,而且这个 lab “奇技淫巧”的感觉多一些,认真思考后见识一下答案并没有坏处
CSAPP官网:http://csapp.cs.cmu.edu/3e/labs.html
答案版本1:https://github.com/Exely/CSAPP-Labs
答案版本2:https://www.zhihu.com/column/c_1325107476128473088
答案版本3:https://www.bilibili.com/video/BV183411k7VM?p=4
题目一:bitXor
题目描述:请仅使用 ~ 和 & 实现 ^ 操作
允许的操作符:~ &
允许的最大操作数:14
题目分析:德摩根律的运用
题目解答:
题目二:tmin
题目描述:请返回补码表示的最小integer值
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:4
题目分析:integer为32bit,为方便分析,以4bit为例,4bit补码中最小的数为-8,即1000,观察可知是由0001左移3位得到
题目解答:
题目三:isTmax
题目描述:判断x是否为最大integer值,是的话返回1,否则返回0
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:12
题目分析:integer为32bit,为方便分析,以4bit为例,最大补码值为7,即0111,观察0111有何独特的特征?0111 + 1 的结果和 0111 按位取反的结果是相同的,都是1000,即 (0111 + 1) ^ (~0111) = 0 ,判断两个值相等会经常用到“两数相等异或结果为0”的原理。出于一些对于位操作敏感的嗅觉,还会发现除了0111,1111也具有这个特征,所以还应该排除1111这种情况
题目解答
题目四:allOddBits
题目描述:判断一个数的奇数位是否为1,是的话返回1,否则返回0
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:10
题目分析:若输入为0xAAAAAAAA,即返回1,以4bit为例,思考1010的结构有什么特点,即如何判断一个数是不是101010...这样的结构,要判断一个数字是不是1010结构,只需要判断这个数和 1010 相 & 的结果是不是 1010 即可,比如1000,他和1010相&的结果是 :1000 & 1010 = 1000,即1000破坏了1010这种结构,得到了1000。
使用 ^ 判断相等,传统艺能了。
作业要求常量不能大于255即0XFF,所以还有一个问题就是如何得到 0xAAAAAAA? 借用移位的妙招,先构造允许的AA,然后左移为AA00,和AA相加,得到AAAA,然后依次循环得到AAAA
题目解答
题目五:negate
题目描述:请返回入参x的相反数
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:5
题目分析:相反数,形式很简单,但是背后却有很深的原理,~x+1,顺便一提,以4bit为例,按位取反加1还是自己的数有两个(0000 和 1000)0和最小值-8
题目解答:
题目六:isAsciiDigit
题目描述: return 1 if 0x30 <= x <= 0x39
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:15
题目分析:间接使用减法
题目解答
题目七:conditional
题目描述:使用位操作实现三元运算符 x ? y : z
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:16
题目解答
题目八:isLessOrEqual
题目描述:if x <= y then return 1, else return 0
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:24
题目分析:直接使用y-x反而会复杂,所以分情况讨论
题目解答
题目九:logicalNeg
题目描述:实现逻辑“非”,使用除了!之外的一切操作符
允许的操作符:! ~ & ^ | + << >>
允许的最大操作数:12
题目解答
题目十:howManyBits(此题我分析的不好,建议看其他人的)
题目描述:返回最小的数字-可以表示一个int类型数字的补码
思路:
从右边的最低位一直往左找,直到找到最左边的1
题目十一:floatScale2
题目描述:入参是unsigned类型的32bit数字,但我们做题人要把这32bit想象成 一个浮点数来对待,因为要求出参为这个浮点数*2 的32bit表示
题目分析:
本题考察对IEEE754标准的是否掌握,需要根据IEEE754的标准,分别取出float中的1bit符号位、8bit解码位、23bit小数位
接下来是最关键的地方,要分情况讨论,至于为什么要分情况讨论,我觉得是出于阅读了IEEE754标准后的直觉、再加上一点聪明的头脑、或者是Google了答案哈哈哈
根据754标准,依据exp的不同,把浮点数划分为:规格化、非规格化、特殊值 三类:
非规格化的值
在非规格化值的情况下,如何表示2*f ,观察浮点数表达式:
$$
V = (-1)^s \cdot M \cdot 2^E
$$
很显然只要在E的基础上加1,最后的结果就是2倍。
以这个数字为例,exp的8bit都是0,所以这是非规格化的浮点数,E = 1 - Bias = -126,M = 0.f = 1/2 ,V = 2^(-127):
$$
0, 0000 0000, 100 0000 0000 0000 0000 0000 = 2^{-127}
$$
E+1后,exp的8bit就是0000 0001 ,导致整个数变为规格化的浮点数,E = e - Bias = -126,M = 1.f = 1.5 ,V = 2^(-126) * 1.5:
$$
0, 0000 0001, 100 0000 0000 0000 0000 0000 = 2^{-126} \cdot 1.5
$$
发现不对了,没有变为2倍,而是变成了1.5倍!所以这启发了我们,对于非规格化的浮点数,若使用E+1这种方案,就需要改变exp的解码,但是exp解码一改变,就变成了规格化的浮点数,这就导致M的解释方法变了(从M=0.f变为M=1.f),最终导致计算出来的V值不是乍看上去的2倍了,所以就不要考虑在2^E项上做手脚了,那就考虑让M变为2倍,M作为一个二进制小数,是可以通过左移变为2倍的
然后检查一下边界情况,如果左移把位数M的最高位1移没了怎么办:(即左移把一个非规格化的值变成了一个规格化的值)
$$
0, 0000 0000, 100 0000 0000 0000 0000 0000= 2^{-127}
$$
左移1bit后变成了规格化的值:
$$
0, 0000 0001, 000 0000 0000 0000 0000 0000= 2^{-126}
$$
这得益于IEEE754标准中规格化的值和非规格化的值之间过渡平滑的特性,所以得到结论,如果是非规格化的值,要使用左移,来求得2*f的32bit正确表达
规格化的值
对于规格化的值,就可以通过E+1的方式,但要考虑一下边界情况:比如我们很快能想到exp为1111 1110的情况,E+1后,规格化的值左移变成了特殊值,以这个数字为例:
这是规格化的浮点数,E = e - Bias = 127,M = 1.f = 1.0 ,V = 1:
$$
0, 1111 1110, 000 0000 0000 0000 0000 0000 = 2^{127}
$$
E+1后,变成了正无穷:
$$
0, 11111111, 000 0000 0000 0000 0000 0000 = 正无穷
$$
发现也是合理的,因为float的规格化值上限就是(2-δ)* 2^{127}, 所以2^128在float看来就是会溢出为正无穷
总结:
题目十二:floatFloat2Int
题目描述:要求返回浮点数f的表达式 (int)f 的等效位级表示。入参是unsigned类型的32bit数字,但我们做题人要把这32bit想象成 一个浮点数来对待,因为要求出参为这个浮点数的转换为int后的值,超过范围的返回 0x80000000u
举个例子,输入为:
$$
0,10000010,00110100000000000000000
$$
这32bit按照IEEE754标准代表浮点数9.625:
$$
V = (-1)^s \cdot M \cdot 2^E = 1\cdot1.001101\cdot2^{130-127} = (1+1/8+1/16+1/64) \cdot2^3 = 9.625
$$
在C语言中,浮点数转整形是向零舍入的,所以
$$
int(9.625) = 9
$$
输出应为:
$$
0000 0000, 0000 0000, 0000 0000,00001001
$$
题目分析
对于绝对值小于1的浮点数,返回的值应该是0
对于绝对值大于1的浮点数,返回的值是这个浮点数的整数部分
按照上一题的思路,尝试进行分类讨论,依旧是先取出三元组:符号位、阶码位、尾数位:
这道题用IEEE754浮点数公式的“二进制理解”更容易理解,回顾公式:
$$
V = (-1)^s \cdot M \cdot 2^E
$$
这个公式可以理解成将M代表的二进制小数(1.xxx或0.xxx)的小数点左移或者右移,比如:
$$
V = (-1)^0 \cdot 1.0011 \cdot 2^3
$$
就可以解释为将1.0011的小数点往右移动3bit,所以V的值就是$1001.1$,而小数点左边的1001就是我们要的整数答案
所以这题的解题思路就是:想办法得到小数点移位结束之后的、小数点左边的二进制表示的十进制数字(比如上面这个例子中的1001)
分情况讨论的关键在于:E的值,即小数点到底右移、左移几位?
情况1:E < 0,由于M是1.xxx或0.xxx,M表示的二进制小数 小数点左边只有一位,E小于0表示小数点至少往左边移动一位,所以移动完成后,小数点的右边一定是0,即整数部分是0,这种情况下,直接返回0就是要求的答案。
情况2:E>= 32,表示M一定是1.xxx(因为E等于-126时M才是0.xxx),如果E >= 32,表示小数点要往右移动32bit,但是int类型最多32bit,所以会爆掉,按照题目要求返回 0x80000000u
情况3:0 <= E < 32
这里需要先把frac23bit前面的1补上,即$(1<<23) | frac$,这个表达式代表M,如M = 1.11001,则这个表达式就是11100100......000(共24bit)
当E>=23时,表示小数点右移可以把M小数点右边的23bit小数都吃掉,还能给后面继续补0,所以直接返回24bit+(补的几个0) 即可。
当E<23时,表示小数点右移不能把M小数点右边的23bit小数都吃掉,只能吃掉一部分,所以返回24bit右移“割舍的bit”
总结:
题目十三:floatPower2
题目描述:要求返回浮点数f的表达式 2.0^x 的等效位级表示。入参是 int 类型的32bit数字,出参是unsigned类型;结果太小的话返回0,太大的话返回+INF
题目分析:本题考察浮点数的表示范围。与前面两道题目不同,入参是一个int类型的数字,前两题入参形式上是unsifgned,但是出题人要求我们看成32bit的单精度浮点数float,然后去表达2*f、int(f)等的位级表示;但是此题输入的确就是一个整型,题目要求也是把入参看成整型。但是出参是一个浮点数
先回答一个问题,2.0x次方长什么样,float如何存储2.0x?
情况一,都是0:
……
2-150的二进制表示为0|00000000|00000000000000000000000
情况二,阶码0,可以观察到是由1左移获得的,从理论上分析是因为M = 0.000x,E = -126
-------------------------------------------------------------------
2-149的二进制表示为0|00000000|00000000000000000000001
2-148的二进制表示为0|00000000|00000000000000000000010
2-147的二进制表示为0|00000000|00000000000000000000100
……
2-129的二进制表示为0|00000000|00100000000000000000000
2-128的二进制表示为0|00000000|01000000000000000000000
2-127的二进制表示为0|00000000|10000000000000000000000
情况三,阶码非0:
-------------------------------------------------------------------
2-126的二进制表示为0|00000001|00000000000000000000000
2-125的二进制表示为0|00000010|00000000000000000000000
2-124的二进制表示为0|00000011|00000000000000000000000
……
20 的二进制表示为0|01111111|00000000000000000000000
21 的二进制表示为0|10000000|00000000000000000000000
……
2127 的二进制表示为0|11111110|00000000000000000000000
情况四,无穷大:
2^128 的二进制表示为0|11111111|00000000000000000000000
2^129 的二进制表示为0|11111111|00000000000000000000000
看到本题要敏感,2.0的x次幂,正好是IEEE754标准的最后一项:
$$
V = (-1)^s \cdot M \cdot 2^E
$$
只要保证M是1.000......00,s是0,或者(M是0.000x,小数点右边只有一个1),最后的结果就是本题要求的结果,所以尝试把入参x看作最后一项的E来进行思考
单精度浮点数的表示范围如上图所示:
分析题目,2.0x一定是个正数,所以最后的表达式一定大于0,float的符号位一定是0,就从图上零点右边开始分析:
情况1:非规格化值能表示的最小2.0x是2-149,所以x<-149时,float无法表示,直接返回0
情况2:规格化值能表示的最小2.0x是2-126,所以-149 <= x < -126时,是非规格化的值可以表示的,非规格化的值特点是阶码exp为0,E = 1 - Bias = -126,,比如x是-130,$V = 2^{-126} * 2^{-4}$,所以M = 2-4,要返回2-130的float表示就是0 ,0000 0000, 000 01000 00000 00000 00000,可以看到是1左移了19bit,19=23-4
情况3:规格化值能表示的最大2.0x是2127,所以 -126 <= x <= 127时,要使V是2.0x,需要M是1.000......00,这个很简单,直接把exp的值<<23bit即可,exp = E +Bias(E就是x)
情况4:x > 127时,float无法表示了,返回+INF
__EOF__

本文链接:https://www.cnblogs.com/looking-for-zihuatanejo/p/15993893.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」