I Data Lab

万事开头难,尤其是和 0 与 1 打交道,和后面的实验相比,这次只能算个热身。但是喜欢运动的都知道,热身很重要!


任务目标

我们先来看看 Datalab 需要我们做什么。主要是通过这次的作业来熟悉整型及浮点数的位表达形式,简单来说,就是解开一些人工谜题。列表如下:

比特操作谜题整数运算谜题:

浮点数运算谜题:

上手指南

一共 13 个需要补充的函数,所有的工作都只需修改 bits.c 文件,测试的话有三种方式:btestdlc, 和 BDD checker

一些小技巧:

  • 在函数开始时声明所有变量
  • } 应该在第一列
  • 注意运算符号的优先级,使用括号确保顺序的正确
  • 关注 !, 0, TMin 等

任务指引还是比较清晰的,主要有以下一些说明:

  1. 整型的范围是 0 到 255(0xFF),不允许用更大
  2. 只能包含参数和局部变量
  3. 一元操作符 ! ~
  4. 二元操作符 & | + << >>

不允许的操作有:

  1. 使用任何条件控制语句
  2. 定义和使用宏
  3. 定义其他的函数
  4. 调用函数
  5. 使用其他的操作符
  6. 使用类型转换
  7. 使用除 int 之外的类型(针对整型)
  8. 使用除 int, unsigned 之外的类型(针对浮点数)

可以认为机器:

  • 使用 2’s complent,32位
  • 执行算术右移
  • 移动超过字长的位数会出问题

其他需要注意的事情有:

  1. 使用 dlc(data lab checker) 来检测代码的合法性(有没有使用不给使用的符号语法等等)
  2. 每个函数都有操作数的上限值,注意 = 不算
  3. 使用 btest 来测试结果的正确与否
  4. 使用 BDD checker 来正规测试你的函数

题目及解法

thirdBits

  • 题目要求:return word with every third bit (starting from the LSB) set to 1
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:8
  • 分值:1

我们返回的结果是:0100 1001 0010 0100 1001 0010 0100 1001,因为题目要求每个变量不可以超过 255,也就是最多 1111 1111,所以只能分步骤来进行组合,如下面代码所示

Desired output: 0100 1001 0010 0100 1001 0010 0100 1001 
Step 1:         0000 0000 0000 0000 0000 0000 0100 1001  0x49
Step 2:         0000 0000 0000 0000 1001 0010 0000 0000  Shift << 9
Step 3:         0000 0000 0000 0000 1001 0010 0100 1001  Add 0x49
Step 4:         0100 1001 0010 0100 0000 0000 0000 0000  Shift << 18
Step 5:         0100 1001 0010 0100 1001 0010 0100 1001  Add result from step 3int thirdBits(void) {int a = 0x49;int b = (a << 9);int c = b + a;return (c << 18) + c; // Steps 4 and 5
}

可以看到第一个函数已经写对的得到了一分,然后我们再来检测一下有没有用非法的操作符:./dlc -e bits.c

可以看到没有显示错误信息,-e 会输出操作符的数量,这里也都没有问题。接下来的题目都会用这种方式测试,但是就不会再贴图了。

isTmin

  • 题目要求:returns 1 if x is the minimum, two’s complement number, and 0 otherwise
  • 允许操作:! ~ & ^ | +
  • 操作数限制:10
  • 分值:1

根据 2’s complement 的定义,Tmin 的值是 10000000 00000000 00000000 00000000,我们要怎么判断一个数是不是 Tmin 呢,原则上来说,只需要把 x 和 Tmin 做 & 操作,判断即可,但是题目要求不能左移,于是就要想其他的办法了,观察 Tmin 的值,发现如果左移一次,就变成全部为 0,但是全部为零的情况还有另外一种就是本身全部就是 0,所以只要排除第二种情况,就可以判断是否是 Tmin 了,代码如下:

// 前面一部分用于判断左移一位后是否全部为0,后面一部分用来判断 x 值是否为 0
int isTmin(int x) {return !(x+x)&!!(x);
}

isNotEqual

  • 题目要求:return 0 if x == y, and 1 otherwise
    • 例如: isNotEqual(5,5) = 0, isNotEqual(4,5) = 1
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:6
  • 分值:2

这题比较简单,发现可以使用异或操作,那么只需要判断两个数异或后结果是否为 0 即可,这里同样使用了 !! 来把 bit 转换成 boolean 值

int isNotEqual(int x, int y)
{return(!!(x ^ y));
}

anyOddBit

  • 题目要求:return 1 if any odd-numbered bit in word set to 1
    • 例如: anyOddBit(0x5) = 0, anyOddBit(0x7) = 1
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:12
  • 分值:2

我们依旧不能超过 0xFF 的限制,所以需要把前面的 24 位都用 | 和 >> 运算符移动到最后八位中,再和 10101010 来做 & 操作,只要不为零,就说明在偶数位上有不为 0 位

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

negate

  • 题目要求:return -x
    • 例如:negate(1) = -1.
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:5
  • 分值:2

第一感觉就是用到取反 ~ 符号,但需要考虑三种情况:正,零,负

  • 假设是 0010(2),取反之后是 1101(-3)
  • 假设是 1110(-2),取反之后是 0001(1)
  • 假设是 0000(0),取反之后是 1111(-1)

可以发现一个规律,就是都差 1,为什么呢,就是因为 2’s complement 的定义中是加上了 1 的,所以只要再加一就好。

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

conditional

  • 题目要求:same as x ? y : z
    • 例如:conditional(2,4,5) = 4
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:16
  • 分值:3

这一题稍微有一些复杂,我们来看看怎么去想。因为不能用 if 来做流程判断,所以我们返回的表达式例一定要包含 y 和 z,但是可以根据 x 的值来进行变换,所以大概的式子是 (y op expr) | (z op expr)(op 表示操作符, expr 是某个表达式)。

然后就简单很多了,我们只要想办法做一个 expr,要么为 0x00000000,要么为 0xffffffff 即可,于是表达式 ~!x + 1 就可以满足我们的需求,x 为 0 时,表达式为 0xffffffff,不等于 0 时也满足条件,就等于有了答案

int conditional(int x, int y, int z) {/**if x!=0,mask=0x00000000,so y&~mask==y and z&mask==0*if x==0,mask=0xffffffff,so y&~mask = y&0 =0; z&mask=z*/int mask= ~!x+1; return (y & ~mask)|(z & mask);
}

subOK

  • 题目要求:Determine if can compute x-y without overflow
    • 例如:
    • subOK(0x80000000,0x80000000) = 1
    • subOK(0x80000000,0x70000000) = 0
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:20
  • 分值:3

这题也不算轻松,但是我们可以一步一步来搞定,首先,既然是计算 x-y,我们要能够知道结果,由于不给使用减号,那就用倒数(之前的方法),所以 x-y 的结果为 ~y+1+x。然后需要怎么判断呢,观察发现,只有在以下这两种情况同时发生的时候,才是 overflow

  1. x 和 y 符号不同
  2. x-y 的符号和 x 不同

可能有点难以理解,overflow 指的是除符号位的最高位进位,也就是说符号会变化,所以需要 x 和 y 的符号不同(这样 x-y 就等同于两个同符号的加法),也就是第一个条件;符号到底有没有变化呢,就要看 x-y 与 x 的符号是否相同,也就是第二个条件。所以代码如下:

int subOK(int x, int y) {/** overflow of sub happens iff * 1) x and y have different signs* 2) res = x - y has different sign with x*/int res = x + (~y + 1);int sameSign = x ^ y;int resSign = res ^ x;return !((sameSign & resSign) >> 31);
}

isGreater

  • 题目要求:if x > y then return 1, else return 0
    • 例如:isGreater(4,5) = 0, isGreater(5,4) = 1
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:24
  • 分值:3

因为要考虑正负号,所以这个问题变成:

  1. 两个数符号相同的情况
  2. 两个数符号不同的情况

具体可以参见代码

int isGreater(int x, int y)
{// Boolean value indicating sign of x// 1 = Negative// 0 = Non-Negativeint sign_x = x >> 31;// Boolean value indicating sign of y// 1 = Negative// 0 = Non-Negativeint sign_y = y >> 31;// if the signs are equal, then// if x is larger, sign bit of (~y + x) is 0// if y is larger or equal to x, sign bit of (~y + x) is 1// 感谢网友 刘镇宽 & AlohaJack 的提醒int equal = !(sign_x ^ sign_y) & ((~y + x) >> 31);// if signs are not equal, these principles are reversed.int notEqual = sign_x & !sign_y;// this | returns 0 when it is x is greater, so you have to negate it.return !( equal | notEqual);
}

bitParity

  • 题目要求:returns 1 if x contains an odd number of 0’s
    • 例如:bitParity(5) = 0, bitParity(7) = 1
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:20
  • 分值:4

这道题要我们统计有有多少个零,这里我们需要利用一个特点,就是堆两个数进行异或操作,不改变奇偶性,所以只需要一步一步来异或就可以了

int bitParity(int x) {/* XORing two numbers returns a number with same bit parity.Keep shifting half of our number until reduced to 1 bit simple case.*/x = ( x >> 16 ) ^ x;x = ( x >> 8 ) ^ x;x = ( x >> 4 ) ^ x;x = ( x >> 2 ) ^ x;x = ( x >> 1) ^ x;return (x & 1);
}

howManyBits

  • 题目要求:return the minimum number of bits required to represent x in two’s complement
    • 例如:
    • howManyBits(12) = 5
    • howManyBits(298) = 10
    • howManyBits(-5) = 4
    • howManyBits(0) = 1
    • howManyBits(-1) = 1
    • howManyBits(0x80000000) = 32
  • 允许操作:! ~ & ^ | + << >>
  • 操作数限制:90
  • 分值:4

这题从操作数限制的数目来看就知道比较复杂,但是代码还是比较清晰的,可以直接查看代码中的注释

int howManyBits(int x) {int temp=x^(x>>31);//get positive of x;int isZero=!temp;//notZeroMask is 0xffffffffint notZeroMask=(!(!temp)<<31)>>31;int bit_16,bit_8,bit_4,bit_2,bit_1;bit_16=!(!(temp>>16))<<4;//see if the high 16bits have value,if have,then we need at least 16 bits//if the highest 16 bits have value,then rightshift 16 to see the exact place of  //if not means they are all zero,right shift nothing and we should only consider the low 16 bitstemp=temp>>bit_16;bit_8=!(!(temp>>8))<<3;temp=temp>>bit_8;bit_4=!(!(temp>>4))<<2;temp=temp>>bit_4;bit_2=!(!(temp>>2))<<1;temp=temp>>bit_2;bit_1=!(!(temp>>1));temp=bit_16+bit_8+bit_4+bit_2+bit_1+2;//at least we need one bit for 1 to tmax,//and we need another bit for signreturn isZero|(temp&notZeroMask);
}

float_half

  • 题目要求:Return bit-level equivalent of expression (float) x. Result is returned as unsigned int, but it is to be interpreted as the bit-level representation of a single-precision floating point values.
  • 允许操作:Any integer/unsigned operations incl. ||, &&. also if, while
  • 操作数限制:30
  • 分值:4

这个就是考察基本的对于 IEEE 浮点数格式的转换了,思路也比较清晰,就是根据不同的部分来求出对应的值

unsigned float_half(unsigned uf) {int round, S, E, maskE, maskM, maskS,maskEM, maskSM, tmp;round = !((uf&3)^3);maskS = 0x80000000;maskE = 0x7F800000;maskM = 0x007FFFFF;maskEM= 0x7FFFFFFF;maskSM= 0x807FFFFF;E = uf&maskE;S = uf&maskS;//Nan or Infinityif (E==0x7F800000) return uf;//E=1 - specialcaseif (E==0x00800000){return S | (round + ((uf & maskEM)>>1)) ;}//E=0 - denormalizedif (E==0x00000000) {tmp = (uf&maskM)>>1;return S | (tmp + round);}//normalized casereturn (((E>>23)-1)<<23) | (uf & maskSM);
}

float_i2f

  • 题目要求:Return bit-level equivalent of expression (float) x. Result is returned as unsigned int, but it is to be interpreted as the bit-level representation of a single-precision floating point values.
  • 允许操作:Any integer/unsigned operations incl. ||, &&. also if, while
  • 操作数限制:30
  • 分值:4

和上题一样,这个就是考察基本的对于 IEEE 浮点数格式的转换了,思路也比较清晰,就是根据不同的部分来求出对应的值

unsigned float_i2f(int x) {/*int exponent=0;return ((sign<<31)|(exponent<<23)|fraction)+flag;*/int sign=x>>31&1;int i;int exponent; int fraction; int delta;int fraction_mask;if(x==0)//||x==0x8000000)return x;else if(x==0x80000000)exponent=158;else{if (sign)//turn negtive to positivex = -x;i = 30;while ( !(x >> i) )//see how many bits do x have(rightshift until 0) i--;//printf("%x %d\n",x,i);exponent = i + 127;x = x << (31 - i);//clean all those zeroes of high bitsfraction_mask = 0x7fffff;//(1 << 23) - 1;fraction = fraction_mask & (x >> 8);//right shift 8 bits to become the fraction,sign and exp have 8 bits totalx = x & 0xff;delta = x > 128 || ((x == 128) && (fraction & 1));//if lowest 8 bits of x is larger than a half,or is 1.5,round up 1fraction += delta;if(fraction >> 23) {//if after rounding fraction is larger than 23bitsfraction &= fraction_mask;exponent += 1;}}return (sign<<31)|(exponent<<23)|fraction;
}

float_f2i

  • 题目要求: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.
  • 允许操作:Any integer/unsigned operations incl. ||, &&. also if, while
  • 操作数限制:30
  • 分值:4

和上题一样,这个就是考察基本的对于 IEEE 浮点数格式的转换了,思路也比较清晰,就是根据不同的部分来求出对应的值

int float_f2i(unsigned uf) {int exp = (uf >> 23) & 0xFF;int frac = uf & 0x007FFFFF;int sign = uf & 0x80000000;int bias = exp - 127;if (exp == 255 || bias > 30) {// exponent is 255 (NaN), or number is too large for an intreturn 0x80000000u;} else if (!exp || bias < 0) {// number is very small, round down to 0return 0;}// append a 1 to the front to normalizefrac = frac | 1 << 23;// float based on the biasif (bias > 23) {frac = frac << (bias - 23);} else {frac = frac >> (23 - bias);}if (sign) {// original number was negative, make the new number negativefrac = ~(frac) + 1;}return frac;
}

总结

这个实验通过各种限制让我们尽可能去寻找不同的解法,相信做完之后对于数据的表示和操作,都会有更深层次的理解。

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

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

相关文章

SQLite 安装使用教程

一、SQLite 简介 SQLite 是一个轻量级的关系型数据库管理系统&#xff0c;嵌入式、零配置、无需安装服务器&#xff0c;广泛应用于移动端开发&#xff08;如 Android&#xff09;、桌面应用、小型网站等场景。 二、下载安装 2.1 官方网站下载 访问 SQLite 官网 下载适用于操…

Python-Word文档、PPT、PDF以及Pillow处理图像详解

Python操作Word和PowerPoint文件操作Word文档命令来安装python-docx三方库。pip install python-docxfrom docx import Document from docx.shared import Inches, Pt, RGBColor from docx.enum.text import WD_ALIGN_PARAGRAPH from docx.enum.table import WD_TABLE_ALIGNMEN…

高可扩展属性建模设计:架构师的全局思考与落地方案

在复杂业务系统中&#xff0c;动态属性扩展始终是架构设计的核心难题之一。传统方案如宽表设计和EAV&#xff08;实体-属性-值&#xff09;模型分别在性能与扩展性上各有优势与劣势&#xff0c;但也都有明显局限。 为了兼顾性能、扩展性、维护成本&#xff0c;需要引入更灵活的…

数据结构入门:链表

链式存储结构通过使用指针将分散的存储单元链接起来&#xff0c;每个元素由数据部分和指针部分组成。 链式表的定义和特点 链式表的每个节点包含两个部分&#xff1a; 数据域&#xff1a;存储数据元素。指针域&#xff1a;存储下一个节点的内存地址。 链式表的头指针指向第一个…

达梦数据库DMHS介绍及安装部署

目录 概述 安装规划 安装步骤 上传安装包 更改权限 执行安装命令 源端和目的端处理 开启归档 开启逻辑日志 创建测试表 生成测试数据 配置目的端文件 配置源端文件 启动目的端 启动源端 装载数据 源端开启cpt模块 数据同步验证 随机数据验证 概述 达梦数据实时同…

BERT 模型详解:结构、原理解析

前言 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;已经成为理解类任务的标配模型。相比 GPT 更擅长文本生成&#xff0c;BERT 则在语言理解任务上展现出卓越的能力。本文…

一、bfv_basics

目录 一、加密参数 EncryptionParameters类1. 三个重要的参数2. 参数的作用3. 同态加密方案4. 多项式模数的度 poly_modulus_degree (n)5. 密文模数 coeff_modulus (q)6. 明文模数 plain_modulus (t&#xff0c;这是 BFV 方案才有的&#xff0c;CKKS 没有) 二、上下文 SEALCont…

AI大模型LangChain架构介绍及其在环保领域的应用

1.LangChain 概述与架构 LangChain 是一个面向大型语言模型&#xff08;LLM&#xff09;应用的开发框架&#xff0c;其核心理念是将复杂的基于语言的 AI 系统拆分为可复用的模块&#xff0c;简化 LLM 与数据源的集成。LangChain 官方文档将其定义为“一个用于开发以 LLM 为驱动…

centos 7 安装NVIDIA Container Toolkit

要在 CentOS 7 上离线安装 NVIDIA Container Toolkit&#xff0c;需确保已安装 NVIDIA 驱动和 Docker 环境。以下是完整步骤及注意事项&#xff1a; ⚙️ 一、环境准备 验证 NVIDIA 驱动 运行 nvidia-smi 确认驱动已正确安装&#xff0c;若未安装需先离线安装驱动&#xff1a; …

C++学习之STL学习:list的使用

本篇我们将学习STL中list的使用 目录 list的初始和官方文档 list的官方文档 list的构造与析构 构造函数 析构函数 运算符重载 迭代器 正向迭代器 反向迭代器 const正向迭代器 const反向迭代器 容量 empty size max_size 访问 访问第一个元素​编辑 访问最后一个元素 修…

USB服务器在证券公司虚拟化进程中的应用分析

在证券公司全面拥抱虚拟化、云化的技术浪潮中&#xff0c;一个看似微小却至关重要的环节曾长期阻碍进程&#xff1a;分散在各业务环节的银行前置机U盾、各种系统认证Ukey等物理USB安全设备的管理难题。这些承载着资金划拨、交易认证核心权限的“小钥匙”&#xff0c;在传统模式…

网闸内部架构设计:分层与微服务的生死博弈

引言 “物理隔离是网闸的命脉,而架构设计决定其生死。” 在数据安全领域,网闸(安全隔离与信息交换系统)是守护核心网络的钢铁长城。但当开发者试图将现代架构思想(如微服务)引入其内部时,却可能引发灾难性冲突。本文通过深度拆解分层架构与微服务在网闸中的适用性,揭示…

通过MaaS平台免费使用大模型API

文章目录 一、引言&#xff1a;MaaS平台——免费使用大模型API的新选择二、模型代码与限制术语详解&#xff08;一&#xff09;模型代码含义解析&#xff08;二&#xff09;模型使用限制术语缩写详解 三、5个MaaS平台详细介绍&#xff08;一&#xff09;OpenRouter&#xff08;…

进程代理单窗口单IP技术:原理、应用与实现

“在当今数字化时代&#xff0c;网络隐私保护与多账号管理需求日益增长。单窗口单IP技术通过为每个进程分配独立网络身份&#xff0c;巧妙地解决了多账号管理中的IP关联难题。从游戏多开防封到数据采集优化&#xff0c;从隐私保护到测试验证&#xff0c;这项技术的应用场景不断…

Java教程——线程池和future

Future 详解 1. Future 是什么? Future 是 Java 中的一个接口(java.util.concurrent.Future),代表异步计算的未来结果。它允许你: 提交任务后立即返回在需要时检查任务是否完成获取任务结果(完成后)取消任务2. 怎么使用 Future? 通过线程池提交任务: ExecutorServ…

洛谷P1351 [NOIP 2014 提高组] 联合权值

洛谷P1351 [NOIP 2014 提高组] 联合权值 洛谷题目传送门 题目背景 NOIP2014 提高组 D1T2 题目描述 无向连通图 G G G 有 n n n 个点&#xff0c; n − 1 n-1 n−1 条边。点从 1 1 1 到 n n n 依次编号,编号为 i i i 的点的权值为 W i W_i Wi​&#xff0c;每条边的长…

Apache Doris Profile 深度解析:从获取到分析,解锁查询性能优化密码

在 Doris 数据库中&#xff0c;高效的查询性能是数据处理的关键。当我们遇到查询缓慢、资源消耗异常等问题时&#xff0c;Doris 提供的 Profile 工具就如同一位 “性能侦探”&#xff0c;能帮我们抽丝剥茧&#xff0c;找到问题根源。今天&#xff0c;我们就来深入聊聊如何分析 …

系统架构师

硬件&#xff1a; 运算器&#xff1a;1&#xff09;算术运算 加减乘除 2&#xff09;逻辑运算并进行逻辑测试&#xff1a;与或非 组件功能&#xff1a;算术逻辑单元ALU :处理数据 实现对数据的算术运算和逻辑运算 累加寄存器AC 通用寄存器&#xff0c;alu提供工作区 暂存运算结…

Unity HDRP + Azure IoT 工业设备监控系统实例

Unity HDRP Azure IoT 工业设备监控系统实例 下面是一个完整的工业设备监控解决方案&#xff0c;结合Unity HDRP&#xff08;高清渲染管线&#xff09;的高质量可视化与Azure IoT的实时数据处理能力。 系统架构 #mermaid-svg-XJnD6acrBbtbqYHW {font-family:"trebuchet…

(超详细)数据库项目初体验:使用C语言连接数据库完成短地址服务(本地运行版)

数据库项目初体验&#xff1a;使用C语言连接数据库完成短地址服务&#xff08;本地运行版&#xff09; 前言&#xff1a;初学者的思考 作为一个刚初学数据库的小白并且在之前我的博客中我有尝试使用C语言写过一个短地址服务&#xff0c;但是使用C语言编写的短地址服务只有短记…