热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CSAPPDataLab做题记录(上)

CSAPPDataLab做题记录(上)准备工作访问http:csapp.cs.cmu.edu3elabs.html试图下载网页上醒目的datalab.tar,发现需要身份验证。后来

CSAPP Data Lab 做题记录(上)

准备工作

访问 http://csapp.cs.cmu.edu/3e/labs.html 试图下载网页上醒目的 datalab.tar,发现需要身份验证。后来发现点后面的小东西可以直接下载。读了 readme 之后知道 datalab.tar 好像是教师用的,用来生成学生的包(datalab-handout),datalab-handout 才是学生用的。

datalab 的神秘链接

读文档,知道实验是要用位运算来模拟各种整数运算,还要用整数运算模拟浮点运算。好像评测还实现了一个小工具来查是否用到了违规的操作符。

看文档上说需要安装 bison 和 flex,以为检查违规小工具也需要编译,正打算看一下代码发现包里直接发了个二进制文件下来……

发现评测程序是用 Perl 写的,古老。

Driverlab.pm 里好像手写了一个 http 客户端?看起来是搞那个 Beat the Prof 比赛的,应该不用管它。

依照手册指示,要先 make 一下把 btest 给编译好。结果遇到神奇问题:

% 09:19:42 jyi@Syameimaru-Aya ~/s/c/d/c/d/datalab-handout
0 make
gcc -O -Wall -m32 -lm -o btest bits.c btest.c decl.c tests.c
In file included from btest.c:16:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory
27 | #include
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
In file included from decl.c:1:
/usr/include/stdio.h:27:10: fatal error: bits/libc-header-start.h: No such file or directory
27 | #include
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
In file included from /usr/lib/gcc/x86_64-linux-gnu/10/include/limits.h:195,
from /usr/lib/gcc/x86_64-linux-gnu/10/include/syslimits.h:7,
from /usr/lib/gcc/x86_64-linux-gnu/10/include/limits.h:34,
from tests.c:3:
/usr/include/limits.h:26:10: fatal error: bits/libc-header-start.h: No such file or directory
26 | #include
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make: *** [Makefile:11: btest] Error 1

检查了一发发现是 makefile 里指定了 -m32 但是我没有 32 位的库,装了个 gcc-multilib。至于为啥不用 -m64 编译……因为那里面说什么 "not 64-bit safe",没太懂。


题目列表


bitXor

给定两个数,返回他们的异或。

首先由真值表写出把异或运算写成最小项之和的形式,就是 \(X \oplus Y = X\bar{Y} + \bar{X}Y\)。然后跑跑发现零分,是因为我们没有 | 可以用……用德摩根定律画画柿子得到 \(\bar{\bar{X\bar{Y}}\bar{\bar{X}Y}}\),避开 |,就能过了。

int bitXor(int x, int y) {
return ~(~(~x & y) & ~(x & ~y));
}

tmin

返回最小的有符号整数。

题目假设机器使用补码表示法,我们知道这个数的位模式应该长得比较像 1000...000。题目又假设了我们机器上的整数都是 32 位的,所以我们把 1 左移 31 位返回就行了。

int tmin(void) {
return 1 <<31;
}

isTmax

判断给定的 x 是不是最大的有符号整数。

考虑到题目假定机器采用补码表示法,最大的有符号整数 tmax 的位模式应该是 01111...111。好像把 tmin 的结果取反就是了,但是他没给 << 操作符,题目又禁止使用超过 255 的整数,所以 tmax 应该搞不出来。

倒是有一个检查加一向上溢出(假设溢出的行为和无符号整数差不多)取反后是不是和自己相等(~(x + 1) == x)的想法,但是有符号数溢出好像是 ub 啊。题目也没有规定溢出会采用什么方式。

先这么做好了……-1 要特判一下因为 -1 取反加一后马上就溢出了。

int isTmax(int x) {
return (!((~(x + 1)) ^ x)) & (!!(x + 1));
}

allOddBits

判断给定 x 的奇数二进制位上是否全为 1。

……这样吗?

int allOddBits(int x) {
return (x >> 1) & (x >> 3) & (x >> 5) & (x >> 7) & (x >> 9) &
(x >> 11) & (x >> 13) & (x >> 15) & (x >> 17) & (x >> 19) &
(x >> 21) & (x >> 23) & (x >> 25) & (x >> 27) & (x >> 29) &
(x >> 31) & 1;
}

测了一下发现性能分数没有拿到,最多只能使用 12 个操作符,而这里用了 33 个。考虑进行优化。因为我们可以直接使用 8 位整数,所以我们可以考虑将输入的东西每 8 位与一下,再用一个掩码检查得到的东西奇数位上是否都为 1。这样就能减少计算了。

int allOddBits(int x) {
return !((x & (x >> 8) & (x >> 16) & (x >> 24) & 0xaa) ^ 0xaa);
}

只用了 9 个操作符耶!


negate

求给定数的相反数。

这个可以使用我们熟知的小结论,把 x 取反后再加一直接得到结果,非常简单。

int negate(int x) {
return (~x) + 1;
}

isAsciiDigit

判断给定输入是不是 ASCII 编码中的数字。

我们已经有了比较两个数字是否相等的便利算法,只要打表检查是否等于每个可能的数字,将结果或起来就是最终答案。但是这样显然是拿不到性能分的嘛。

嗯……如果 x 的最高位为 1,那它是个负数,就不是 ASCII 的数字了。排除这种情况后,接下来就只要考虑正数比大小。

发现异或的结果的最高位是两个数字第一个不同之处,找出谁是 1 谁就更大。问题就变成了如何取一个数的最高位。并没有想到什么取一个数最高位的便利算法……

考虑利用一下题目特性,ASCII 的 0 和 9 是 110000 和 111001,想到搞一个东西来检查 x 的前面 26 位是否全为 0,而且第 5、6 位为 1。接着再判断后四位是否符合第四位为 0,或者第 2、3 位为零……

折腾一下得到这样的答案:

int isAsciiDigit(int x) {
return (!((~0x3f) & x)) & (!((0x30 & x) ^ 0x30)) &
(!(0x8 & x) | (!(0x6 & x)));
}

轻松过关


conditional

实现类似三目运算符的功能。

要实现 x ? y : z 的话,应该很容易想到 (!!x) * y + (!x) * z 这种的……但是我们没有乘号。可以想到使用与号替代一下,就是想办法弄一个掩码,当 x 为真时它是全 1,x 为假时它是全零这种的。感觉比较简单。

代码里为了节省符号把掩码弄成当 x 为真时是全 0,x 为假时是全 1。使用方法还是差不多的吧。

int conditional(int x, int y, int z) {
int mask = !x;
mask = (mask <<1) | mask;
mask = (mask <<2) | mask;
mask = (mask <<4) | mask;
mask = (mask <<8) | mask;
mask = (mask <<16) | mask;
return ((~mask) & y) | (mask & z);
}

写后面的 howManyBits 的时候获得了重大技术革新,现在有一种便利操作来生成掩码了!

int conditional(int x, int y, int z) {
int mask = (~(!x)) + 1;
return ((~mask) & y) | (mask & z);
}

如果 x 是 0,则 ~(!x) 位模式为 1111...1110,加 1 后刚好是全 1。

如果 x 是 1,则 ~(!x) 位模式为 1111...1111,加 1 向上溢出后刚好是全 0。


isLessOrEqual

判断给定的两个数是否满足小于等于关系。

小于等于就是不大于嘛,接下来考虑判断两个数的大于关系。

这个好像在 isAsciiDigit 那个题里的踩过一次,所以接着思路往下想……如何取一个数的最高位。发现取最高位的话怎么写都会拿不完性能分。

突然想到好像两个数相减一下再判断结果是否为正数就行,原来刚刚想那么多是脑子打结了。

先判断两个数是否正好为一正一负,如果是的话可以直接给结果。否则相减判断结果正负,这时两个同号的数相减必不可能溢出。

int isLessOrEqual(int x, int y) {
return (((x >> 31) & 1) | (!(y >> 31))) &
((((x >> 31) & 1) & (!(y >> 31))) | (!((y + (~x) + 1) >> 31)));
}

拿下。


logicalNeg

实现逻辑非,不能使用感叹号。

感觉和 allOddBits 很像!只不过那个是求奇数位上全为 1,这里是求存在某一位为 1。

用类似的方法实现一下就好啦。

int logicalNeg(int x) {
x = x | (x >> 16);
x = x | (x >> 8);
x = x | (x >> 4);
x = x | (x >> 2);
x = x | (x >> 1);
return 1 ^ (x & 1);
}

最后 return 那个奇怪的表达式其实是 1 - x 拆过来的。发现直接写 2 + (~x) 会拿不到性能分,符号刚好多一个。


howManyBits

求最少用多少个位可以表示出给定数字。

其实就是 \(\log_{2}\) 啦。

先假定 x 是负数,根据补码表示法我们需要能够表示 \([ x, -x - 1 ]\) 的所有数。全部当成无符号整数之后需要表示的范围是 \([0, ((~x) <<1) + 1]\),只要我们能够用一些位表示出最大的数,那么这些位一定可以表示出所有数。因此答案就是 \(\log_{2}(((~x) <<1) + 1)\)(向上取整)。

同理,如果 x 是正数,则需要能够表示 \([ -x - 1, x ]\) 的所有数,答案是 \(\log_{2}((x <<1) + 1)\)

至于如何取对数……猜测是要使用类似 logicalNeg 和 allOddBits 那样类似分治(?)的做法来完成,先考虑分成两半的情形:如果高 16 位不为零,可以给答案加上 16,接着再把高 16 位移动到低 16 位,按照类似的方式处理低 16 位;如果高 16 位为零,则直接处理低 16 位。依次类推直到处理完只剩一位的情况。

用力实现一下就好了。

int howManyBits(int x) {
int ans = 0;
int h16, h8, h4, h2, h1, h0;
int sign = x >> 31;
int range = (((x & (~sign)) | ((~x) & sign)) <<1) + 1;
h16 = (~(!!(range >> 16))) + 1;
ans = ans + (h16 & 16);
range = range >> (h16 & 16);
h8 = (~(!!((range >> 8) & 0xff))) + 1;
ans = ans + (h8 & 8);
range = range >> (h8 & 8);
h4 = (~(!!((range >> 4) & 0xf))) + 1;
ans = ans + (h4 & 4);
range = range >> (h4 & 4);
h2 = (~(!!((range >> 2) & 0x3))) + 1;
ans = ans + (h2 & 2);
range = range >> (h2 & 2);
h1 = (~(!!((range >> 1) & 0x1))) + 1;
ans = ans + (h1 & 1);
range = range >> (h1 & 1);
h0 = (~(range & 0x1)) + 1;
ans = ans + (h0 & 1);
return ans;
}

真不容易!



推荐阅读
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • WebSocket与Socket.io的理解
    WebSocketprotocol是HTML5一种新的协议。它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • TPL实现Task.WhileAll扩展方法
    文章翻译整理自NikolaMalovic两篇博文:Task.WhileAllAwaitabletaskprogressreporting当Task.WhenAll遇见 ... [详细]
  • 深入理解计算机系统之链接(一)
    程序是怎样运行的写好的c程序怎样运行的呢?答案是一个写好的程序要先经过语言预处理器,编译器,汇编器和链接器生成最后的可执行文件,然后加载器将可执行文件加载到内存中才能运行。这里以一 ... [详细]
  • c语言 怎么访问64位地址_C语言调动硬件的原理是什么?
    大家都知道我们可以使用C语言写一段程序来控制硬件工作,但你知道其工作原理吗?1c语言在实际运行中,都是以汇编指令的方式运行的,由编译器把C ... [详细]
  • 转自:http:www.phpweblog.netfuyongjiearchive200903116374.html一直对字符的各种编码方式懵懵懂懂,什 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • switch语句的一些用法及注意事项
    本文介绍了使用switch语句时的一些用法和注意事项,包括如何实现"fall through"、default语句的作用、在case语句中定义变量时可能出现的问题以及解决方法。同时也提到了C#严格控制switch分支不允许贯穿的规定。通过本文的介绍,读者可以更好地理解和使用switch语句。 ... [详细]
  • 加密世界下一个主流叙事领域:L2、跨链桥、GameFi等
    本文介绍了加密世界下一个主流叙事的七个潜力领域,包括L2、跨链桥、GameFi等。L2作为以太坊的二层解决方案,在过去一年取得了巨大成功,跨链桥和互操作性是多链Web3中最重要的因素。去中心化的数据存储领域也具有巨大潜力,未来云存储市场有望达到1500亿美元。DAO和社交代币将成为购买和控制现实世界资产的重要方式,而GameFi作为数字资产在高收入游戏中的应用有望推动数字资产走向主流。衍生品市场也在不断发展壮大。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • asp中如何嵌入python的简单介绍
    本文目录一览:1、如何在IIS中执行Python脚本 ... [详细]
  • Ubuntu 用户安装 Linux Kernel 3.15 RC1
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
author-avatar
手机用户2502940575
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有