CSAPP Data Lab

CSAPP 的第一个 Lab,对应知识点为书中的第 2 章(信息的表示与处理),要求使用受限制的运算符和表达式实现一些位操作。主要分为两个部分:整数部分和浮点数部分。其中整数部分限制较多,比较偏重技巧性,部分题个人认为很有难度。而浮点数部分则比较基础,主要考察对 IEEE 754 标准的熟悉程度,代码较长,但思路相对简单。

bitXor

思路

使用德-摩根定律进行推导,推导过程如下:

请添加图片描述

代码

int bitXor(int x, int y) {// 德-摩根定律return ~(~(x & ~y) & ~(~x & y));
}

tmin

思路

最小整数即最高位(负数权重)为 1,其余(正数权重)为 0。

代码

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

isTmax

思路

由于不能使用左移运算符,因此没办法直接构造出 tmax,需要仔细考虑 tmax 的性质:tmax = 0x7fffffff ,而 tmax + 1 = 0x80000000 ,这两个数的二进制位完全互补,因此满足:tmax + tmax + 1 = 0xffffffff,结果全为 1,对该结果取反即可得到 0,取非得到 1。

但这里还要考虑一个特殊情况:当 x = 0xffffffff 时,x + 1 + x 也满足等于 0xffffffff,因此需要借助异或运算进行特判。

代码

int isTmax(int x) {int case1 = !((x + 1) ^ 0);  // case = x == 0xffffffff ? 1 : 0;return !(~(x + 1 + x)) & !case1;
}

allOddBits

思路

首先构造一个掩码 mask,奇数位全为 1,偶数位全为 0。将 mask 与 x 进行按位与,如果 x 的奇数位全为 1,那么按位与的结果仍然为 mask。然后便可以借助异或和非的组合,将结果转换为 0 或 1。

代码

int allOddBits(int x) {int mask = 0xaa;            // mask = 0x000000aamask = (mask << 8) | 0xaa;  // mask = 0x0000aaaamask = (mask << 8) | 0xaa;  // mask = 0x00aaaaaamask = (mask << 8) | 0xaa;  // mask = 0xaaaaaaaareturn !((x & mask) ^ mask);
}

negate

思路

补码表示法的重要特性,取反加一即可。

代码

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

isAsciiDigit

思路

这里我用了比较笨的逐位判断的方法。首先判断第 4 到第 31 位是否为 0x3,然后只需要关注低 4 位的二进制表示了:若第 3 位为 0,则一定位于指定范围之内,再加上两个特例(1000 和 1001)即可。

最后将运算符的个数刚好卡在 15 个,勉强过关。

代码

int isAsciiDigit(int x) {// 0011 0000 <= x <= 0011 1001int high = !((x >> 4) ^ 0x3);         // 4 ~ 31 位是否为 0x3int case1 = ((x & 0xf) >> 3) ^ 0x1;   // bit3 = 0int case2 = !((x & 0xf) ^ 0x8);       // bit0~3 = 1000int case3 = !((x & 0xf) ^ 0x9);       // bit0~3 = 1001return high & (case1 | case2 | case3);
}

conditional

思路

很容易想到根据 x 的值是否非 0 构造出全 0 或者全 1 的数据 flag,然后将 flag 和 flag 取反后的值分别与 y 和 z 进行按位与,这样必然得到两个数:一个为 y 或 z 本身,另一个为 0,再将结果按位或即可。

构造的方法比较巧妙,需要注意到全 0 和全 1 分别代表整数 0 和 -1,它们分别是 0 和 1 的相反数,而 0 和 1 我们可以根据表达式是否非 0,使用非运算符构造出来,再将构造的结果取反加一即可。

代码

int conditional(int x, int y, int z) {int flag = ~(!x) + 1;     // flag = x ? 0 : -1;int yp = ~flag & y;       // flag = 0, yp = y; flag = -1, yp = 0;int zp = flag & z;        // flag = 0, zp = 0; flag = -1, zp = z;return yp | zp;
}

isLessOrEqual

思路

判断两个数的大小关系,很容易想到使用作差的方法,判断 x + ~y + 1 的结果是否小于等于 0,即全为 0 或者最高位为 1。

不过这里还需要考虑溢出:由于同号相减必定不会导致溢出,因此我们只需要考虑异号的情况。而如果两个数异号,那它们之间的大小关系就显而易见了。

代码

int isLessOrEqual(int x, int y) {int sign1 = ((x >> 31) & 1) & ((y >> 31) ^ 1);  // sign1 = (x < 0 && y > 0) ? 1 : 0;int sign2 = ((x >> 31) ^ 1) & ((y >> 31) & 1);  // sign2 = (x > 0 && y < 0) ? 1 : 0;int z = x + ~y + 1;                             // z = x - yint sub = !(x ^ y) | ((z >> 31) & 1);           // z <= 0    return (sub | sign1) & !sign2;
}

logicalNeg

思路

这题从二进制位的角度不好思考,不妨从其表示的十进制数的角度出发:

当 x = 0 时,-x = x ,即 x 和 -x 的最高位相同,都为 0;当 x != 0 时,x 和 -x 的最高位必定有一个为 1。

可以利用这一特性将 x | nx 右移 31 位,由于整数进行的是符号右移,因此当最高位为 0 时,右移的结果全为 0,当最高位为 1 时,右移的结果全为 1。再将右移结果加 1,即可构造出 1 或者 0,且刚好与零和非零对应。

代码

int logicalNeg(int x) {int nx = ~x + 1;return ((x | nx) >> 31) + 1;
}

howManyBits

思路

这题看到限制 90 个运算符就给吓着了,实际上也确实很困难,自己想了半天也没有思路,于是在网上参考了别人的解法,感觉相当精妙,在这里介绍一番:

对于正整数 x 而言,可以使用二分搜索的方式来确定所需的位数。首先判断 x 是否需要 16 位来表示,即 x 右移 16 位是否为 0,如果是,则右移 16 位,否则不做处理,然后再判断是否需要 8 位来处理,以此类推。最后将上述过程中的右移次数累加起来再加一(正整数首位需要为 0),即为总共需要的位数。

对于负整数 x 而言,它所需的位数与 x 取反得到的整数所需位数相同,证明没整明白。。。

代码

int howManyBits(int x) {int absx = (x >> 31) ^ x;int b16, b8, b4, b2, b1, b0;// 二分搜索b16 = (!!(absx >> 16)) << 4;absx = absx >> b16;b8 = (!!(absx >> 8)) << 3;absx = absx >> b8;b4 = (!!(absx >> 4)) << 2;absx = absx >> b4;b2 = (!!(absx >> 2)) << 1;absx = absx >> b2;b1 = (!!(absx >> 1));absx = absx >> b1;b0 = absx;return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}

floatScale2

思路

这题主要是要对规格化数和非规格化数进行分类讨论:

当 uf 为规格化数,即阶码不为 0 时,乘二相当于将阶码位加 1。

当 uf 为非规格化数,即阶码为 0 时,此时 uf 的值完全由尾数来表示,且不含隐含 0,因此乘二相当于将尾数乘二,即左移 1 位。

需要注意的是,当 uf 为非规格化数且尾数最高位为 1 时,尾数左移会导致最高位的 1 移动到阶码的最低位。但经过验证,此时的结果仍然符合预期,即非规格化数无缝衔接到了规格化数,不禁感叹 IEEE 754 标准浮点数的设计之精妙。

代码

unsigned floatScale2(unsigned uf) {unsigned t = (uf >> 23) & 0xff;  // 阶码unsigned m = uf & 0x7fffff;      // 尾数if (t == 0xff) return uf;        // 无穷大或者 NaN// 非规格化数if (t == 0x00) {m <<= 1;uf &= 0xff800000;uf |= m;}// 规格化数else {t += 1;uf &= 0x807fffff;uf |= (t << 23);}return uf;
}

floatFloat2Int

思路

首先确定整数所能表示的上下界的值:当阶码小于 127,即指数位小于 0 时,此时浮点数 uf 小于 1,对应的整数为 0;当阶码大于 150,即指数位大于 23 时,此时单精度浮点数的精度(尾数长度)不足以正确表示对应的整数,返回 0x80000000。

对于在合理范围内的 uf,将其转换为对应的整数,首先需要尾数最高位的高一位加上规格化数隐含的 1,再根据阶码的大小将尾数进行右移,阶码越大,右移位数越少。最后根据符号位的值选择是否将结果取反加一。

代码

int floatFloat2Int(unsigned uf) {unsigned s = (uf >> 31) & 0x1;   // 符号unsigned t = (uf >> 23) & 0xff;  // 阶码unsigned m = uf & 0x7fffff;      // 尾数int val;// 小于 1if (t < 127) {val = 0;}// 大于 1 且不溢出else if (t <= 150) {val = m | 0x800000;val >>= (23 - (t - 127));if (s == 1) {val = ~val + 1;}}// 溢出else {val = 0x80000000;}return val;
}

floatPower2

思路

同样是对规格化数和非规格化数的分类讨论:

x >= -150 && x < -127 时,结果为非规格化数,此时浮点数表示只有一个位为 1,其余全为 0。直接根据指数 x 的值确定该位的位置即可。

x >= -127 && x < 128 时,结果为规格化数,此时浮点数表示的尾数全为 0,只有阶码用来表示指数的值。根据指数 x 的值确定阶码的值,然后构造出浮点数即可。

代码

unsigned floatPower2(int x) {unsigned val;// 太小if (x < -150) {val = 0;}// 非规格化数else if (x < -127) {val = 1 << (150 + x);}// 规格化数else if (x < 128) {unsigned t = x + 127;val |= (t << 23);}// 太大else {val = 0x7f800000;}return val;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/413946.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

点餐收银小程序

一、项目概述 Hi&#xff0c;大家好&#xff0c;今天分享的项目是《点餐收银小程序》。 系统含管理员/商家/用户三种角色&#xff0c;商家能维护菜式类别、维护菜品信息&#xff0c;用户在小程序能够选择门店&#xff0c;查看门店下各个分类的菜式信息&#xff0c;并进行加购…

C语言基础(三十一)

1、线性搜索&#xff1a; #include "date.h" #include <stdio.h> #include <stdlib.h> #include <time.h> // 希尔排序 void shellSort(int arr[], int n) { for (int gap n / 2; gap > 0; gap / 2) { for (int i gap; i < n; i…

智慧党建解决方案

1. 新时代党建工作背景 报告强调了新时代党建工作的重要性&#xff0c;提出要利用互联网、大数据等新兴技术推进智慧党建&#xff0c;提高党的执政能力和领导水平。 2. 基层党组织建设挑战 基层党组织在日常工作中面临组织管理难、过程监管难、宣传教育难等问题&#xff0c;…

2024年【四川省安全员B证】最新解析及四川省安全员B证考试资料

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员B证最新解析是安全生产模拟考试一点通总题库中生成的一套四川省安全员B证考试资料&#xff0c;安全生产模拟考试一点通上四川省安全员B证作业手机同步练习。2024年【四川省安全员B证】最新解析及四川省安…

【卡码网C++基础课 14.链表的基础操作2】

目录 题目描述与分析代码编写 题目描述与分析 题目描述&#xff1a; 请编写一个程序&#xff0c;实现以下操作&#xff1a; 构建一个单向链表&#xff0c;链表中包含一组整数数据&#xff0c;输出链表中的第 m 个元素&#xff08;m 从 1 开始计数&#xff09;。 要求&#xf…

苹果笔记本电脑能不能玩游戏?苹果电脑玩游戏咋样?

过去Mac玩不了游戏最大的问题&#xff0c;就是图形API自成一体&#xff0c;苹果既不支持微软的DirectX&#xff0c;同时为了推广自家的Metal图形API&#xff0c;又对OpenGL和Vulkan两大主流的通用API敬而远之。游戏生态、硬件瓶颈让苹果电脑不适合玩游戏。 不过说到底&#xf…

【数据结构入门】二叉树之堆排序及链式二叉树

目录 前言 一、堆排序 1.概念 2.堆排序思想 3.具体步骤 4.实现 5.复杂度 二、堆的应用——TopK问题 三、链式二叉树 1.二叉树创建 2.二叉树遍历 1&#xff09;前序、中序以及后序遍历 2&#xff09;层序遍历 3.结点个数以及高度 1&#xff09;结点个数&#xff1a…

极狐GitLab 如何管理 Kubernetes 集群?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;专门面向中国程序员和企业提供企业级一体化 DevOps 平台&#xff0c;用来帮助用户实现需求管理、源代码托管、CI/CD、安全合规&#xff0c;而且所有的操作都是在一个平台上进行&#xff0c;省事省心省钱。可以一键安装极狐GitL…

Springboot中使用Elasticsearch(部署+使用+讲解 最完整)

目录 引言 一、docker中安装Elasticsearch 1、创建es专有的网络 2、开放端口 3、在es-net网络上安装es和kibana 4、可能出现的问题 5、测试 6、安装IK分词器 7、测试IK分词器 二、结合业务实战 1、准备依赖 2、配置yml 3、读取yml配置 4、准备es配置类 5、编写测…

【AI学习笔记】AIGC,AI绘画 ComfyUI+ComfyUI Manager安装

【AI学习笔记】ComfyUIComfyUI Manager安装 最近在面向BOSS直聘学习ComfyUI的使用&#xff0c;但是不出意外&#xff0c;因为学习者们迥异的电脑配置以及杂乱的AI软件工具包互相纠缠&#xff0c;跟人工智能相关的环境安装多少都会遇到点教程预料不到的BUG。 推荐入门教程&…

盛京银行营收、利润双降下的负重难行,症结在哪儿?

撰稿|芋圆 来源|贝多财经 盛京银行自2020开年始&#xff0c;经营业绩除了在2022年稍有回暖外&#xff0c;均处于营收、利润双降的局面。 2024年半年报显示&#xff0c;盛京银行的资产总额为10683亿元&#xff0c;规模较2023年末收缩1.1%&#xff1b;营业收入46亿元&#xff0…

如何在windows中使用hfd.sh aria2c下载huggingface文件

这里写目录标题 简介hfd.sh使用方法windows系统安装aria2c aria2c官方文档&#xff1a; https://aria2.github.io/manual/en/html/aria2c.html 简介 我们在下载huggingface上模型权重的时候&#xff0c;要么在浏览器上直接下&#xff0c;要么使用官方下载程序。浏览器上还得一…

easy_spring_boot Java 后端开发框架

Easy SpringBoot 基于 Java 17、SpringBoot 3.3.2 开发的后端框架&#xff0c;集成 MyBits-Plus、SpringDoc、SpringSecurity 等插件&#xff0c;旨在提供一个高效、易用的后端开发环境。该框架通过清晰的目录结构和模块化设计&#xff0c;帮助开发者快速构建和部署后端服务。…

应急响应-应急响应流程(各个阶段与实战)

目录 前言准备阶段检测阶段研判分析定损止损&#xff08;对应遏制、根除阶段&#xff09;定损止损 攻击还原清理恢复总结复盘实战讲解进程ssh暴力破解命令混淆派生恶意命令命令注入 网络文件webshellC2脚本木马 参考 前言 做入侵检测时会有一些攻击告警&#xff0c;需要做应急…

《神话:悟空》的破晓之路:文化深度与技术巅峰的交响乐章

在八月的炽热中&#xff0c;《黑神话&#xff1a;悟空》如同一道璀璨的光芒&#xff0c;划破了国产游戏的寂静夜空&#xff0c;不仅以其惊人的销量速度震撼了业界&#xff0c;更以其深厚的文化底蕴与顶尖的游戏设计&#xff0c;在全球玩家心中留下了不可磨灭的印记。这款游戏的…

鸿蒙XComponent组件的认识

概述&#xff1a; XComponent组件作为一种渲染组件&#xff0c;通常用于满足开发者较为复杂的自定义渲染需求&#xff0c;例如相机预览流的显示、游戏画面的渲染、自定义视频播放器等等。其中Native API是其核心内容&#xff01; 其可通过指定其type字段来实现不同的功能&…

入行「游戏策划」,该从何处下手?

想知道策划岗位该怎么入行可点击蓝链 相比较起以技术为最重要评判标准的开发岗&#xff0c; 「游戏策划」这一岗位在非业界人士的眼中 一直都是一个风评方差很大的岗位。 有人说策划岗又轻松又威风&#xff0c; 只需要输出想法&#xff0c;落地都交给开发&#xff0c; 干…

u盘pe怎么安装系统_u盘pe安装系统详细步骤

u盘pe怎么安装系统&#xff1f;u盘pe安装系统需要准备一个u盘&#xff0c;然后将u盘制作成pe&#xff0c;进入pe后再安装系统&#xff0c;下面小编就教大家u盘pe安装系统详细步骤教程。 u盘pe启动盘是什么&#xff1f; u盘pe启动盘是一种可引导的USB存储设备&#xff0c;其中包…

【js逆向专题】4.python调用JS和扣代码

小节目标: 掌握 python调用js代码方式熟悉 js开放接口进行调用了解 补环境的基本概念掌握 js调试技巧 一. pyexecjs的使用 1. 简介 PyExecJS 是一个 Python 库&#xff0c;用于在 Python 环境中执行 JavaScript 代码。它实际上是对 ExecJS 库的 Python 封装&#xff0c;Exec…

Makefile入门

Makefile入门 文章目录 Makefile入门一、Makefile入门1.1 编译工具及构建工具介绍&#xff1a;1.2 编译的四个阶段&#xff1a;1.3 Makefile的认知&#xff1a;1.3.1 什么是Makefile&#xff1a;1.3.2 Makefile的规则与示例&#xff1a; 二、Makefile的基本语法&#xff1a;2.1…