一、环境配置-windows篇
首先是去官网下载所需文件datalab-handout.tar
看网上的教程都是用的linux系统做的,幸运的是,前两天写毕业论文时抽空学了一点linux,此处本人所用的环境是windows搭配虚拟机VMware,linux的版本是CentOS7.6。
个人步骤记录
1.将文件放入share的共享文件夹
放入共享文件夹,方便linux系统读取,反正都是在本机上使用,就不用xftp这些工具了
windows:位置如图
linux:位置如图
2.给datalab创建文件夹
毕竟后续还有其他的lab,下图已经解压了
使用该命令可以进行解压tar -xvf datalab-handout.tar
,以下为截图
3.试着编译一下,看看环境还差什么
使用make命令可以进行编译,此时出现报错①如下
/usr/include/gnu/stubs.h:7:27: 致命错误:gnu/stubs-32.h:没有那个文件或目录
# include <gnu/stubs-32.h>
^
编译中断。
In file included from /usr/include/features.h:399:0,
from /usr/include/stdio.h:27,
from decl.c:1:
/usr/include/gnu/stubs.h:7:27: 致命错误:gnu/stubs-32.h:没有那个文件或目录
# include <gnu/stubs-32.h>
^
编译中断。
In file included from /usr/include/features.h:399:0,
from /usr/include/limits.h:26,
from /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/limits.h:168,
from /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/syslimits.h:7,
from /usr/lib/gcc/x86_64-redhat-linux/4.8.5/include/limits.h:34,
from tests.c:3:
/usr/include/gnu/stubs.h:7:27: 致命错误:gnu/stubs-32.h:没有那个文件或目录
# include <gnu/stubs-32.h>
^
编译中断。
make: *** [btest] 错误 1
查了一下,是因为这是64位的系统,lab的文件是32位的,要安装C库文件,参考链接
解决报错①办法: 使用如下命令进行安装C库文件
sudo yum install glibc-devel.i686
然后又报错②,我目前该用户没有这个超级管理员的权限
[voiceu@voiceu01 datalab-handout]$ sudo yum -y install glibc-devel.i686
[sudo] voiceu 的密码:
voiceu 不在 sudoers 文件中。此事将被报告。
解决报错②办法: 切换root用户来安装,或者给当前用户超级管理员的权限
显然直接切为root更方便,给权限还是得去root里给 通过 visudo 命令进行修改文件
所以直接 su root 切到root,再如下命令
[root@voiceu01 datalab-handout]# yum -y install glibc-devel.i686
安装成功,继续尝试 make,又报错③
[root@voiceu01 datalab-handout]# make
gcc -O -Wall -m32 -lm -o btest bits.c btest.c decl.c tests.c
/usr/bin/ld: 当搜索用于 /usr/lib/gcc/x86_64-redhat-linux/4.8.5/libgcc_s.so 时跳过不兼容的 -lgcc_s
/usr/bin/ld: 找不到 -lgcc_s
collect2: 错误:ld 返回 1
make: *** [btest] 错误 1
解决报错③办法: 修改Makefile文件中的 -m32 为 -m64
使用vim Makefile进入并修改
修改后保存并退出,继续make,此时不再报错
[root@voiceu01 datalab-handout]# vim Makefile
[root@voiceu01 datalab-handout]# make
gcc -O -Wall -m64 -lm -o btest bits.c btest.c decl.c tests.c
gcc -O -Wall -m64 -o fshow fshow.c
gcc -O -Wall -m64 -o ishow ishow.c
编译成功之前的文件列表
[voiceu@voiceu01 datalab-handout]$ ls
bits.c btest.c decl.c Driverhdrs.pm driver.pl ishow.c README
bits.h btest.h dlc Driverlib.pm fshow.c Makefile tests.c
编译成功之后的文件列表
[root@voiceu01 datalab-handout]# ls
bits.c btest btest.h dlc Driverlib.pm fshow ishow Makefile tests.c
bits.h btest.c decl.c Driverhdrs.pm driver.pl fshow.c ishow.c README
测试运行btest程序
[root@voiceu01 datalab-handout]# ./btest
Score Rating Errors Function
ERROR: Test bitXor(-2147483648[0x80000000],-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test tmin() failed...
...Gives 2[0x2]. Should be -2147483648[0x80000000]
ERROR: Test isTmax(-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test allOddBits(-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test negate(-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be -2147483648[0x80000000]
ERROR: Test isAsciiDigit(-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test conditional(-2147483648[0x80000000],-2147483648[0x80000000],-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be -2147483648[0x80000000]
ERROR: Test isLessOrEqual(-2147483648[0x80000000],-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 1[0x1]
ERROR: Test logicalNeg(-2147483648[0x80000000]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test howManyBits(-2147483648[0x80000000]) failed...
...Gives 0[0x0]. Should be 32[0x20]
ERROR: Test floatScale2(0[0x0]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test floatFloat2Int(0[0x0]) failed...
...Gives 2[0x2]. Should be 0[0x0]
ERROR: Test floatPower2(0[0x0]) failed...
...Gives 2[0x2]. Should be 1065353216[0x3f800000]
Total points: 0/36
可以看见,是成功运行的,说明环境搭建完成
二、进入正题
这个lab的目的,就是写出一些函数来达到一些目的,通过题目的要求,比如只能用异或运算等限制,或者其他的限制,来达到什么目的的输出,以此来加深对数据、信息在计算机中的表达和处理的理解。
其中有两个工具, btest 和 dlc ,btest来检查是否写对,dlc来检查代码是否规范
做题的时候,直接vim bits.c就行,在里面做完保存后用btest来测试即可,不过我对linux还不熟练,总觉得vim来做确实不舒服,还是vscode用惯了
前言注释
bits.c文件里前言注释有如下提醒,精简一下
*
* CS:APP Data Lab
*
* WARNING: Do not include the <stdio.h> header; it confuses the dlc
* compiler. You can still use printf for debugging without including
* <stdio.h>, although you might get a compiler warning. In general,
* it's not good practice to ignore compiler warnings, but in this
* case it's OK.
*/
提示不要包含<stdio.h> ,会混淆dlc工具的编译器?
You will provide your solution to the Data Lab by
editing the collection of functions in this source file.
提示直接编辑这个文件就行
INTEGER CODING RULES:(整数编码规则)
Replace the "return" statement in each function with one
or more lines of C code that implements the function. Your code
must conform to the following style:
要求遵循如下代码规范
int Funct(arg1, arg2, ...) {
/* brief description of how your implementation works */
int var1 = Expr1;
...
int varM = ExprM;
varJ = ExprJ;
...
varN = ExprN;
return ExprR;
}
Each "Expr" is an expression using ONLY the following:
1. Integer constants 0 through 255 (0xFF), inclusive. You are
not allowed to use big constants such as 0xffffffff.
不能用长整型
2. Function arguments and local variables (no global variables).
只能写局部变量
3. Unary integer operations ! ~
一元整数运算允许使用 ! ~
4. Binary integer operations & ^ | + << >>
二进制整数运算允许使用 & ^ | + << >>
Some of the problems restrict the set of allowed operators even further.
Each "Expr" may consist of multiple operators. You are not restricted to
one operator per line.
有些问题可能会限制得更多,每行可以多个操作
You are expressly forbidden to:
1. Use any control constructs such as if, do, while, for, switch, etc.
不能用条件语句
2. Define or use any macros.
不能使用宏
3. Define any additional functions in this file.
不能定义附加的函数
4. Call any functions.
不能调用任何函数
5. Use any other operations, such as &&, ||, -, or ?:
不能用其他的操作符,比如 &&, ||, -, or ?: , 能用的在上面写了
6. Use any form of casting.
不能进行任何形式的类型转换
7. Use any data type other than int. This implies that you
cannot use arrays, structs, or unions.
不能用数组遵循数据类型,不能超过int类型,基本上也就char、int这些了
You may assume that your machine:假设你的机器是
1. Uses 2s complement, 32-bit representations of integers.
使用补码编码表示32位整数的机器
2. Performs right shifts arithmetically.
算术右移的机器
3. Has unpredictable behavior when shifting if the shift amount
is less than 0 or greater than 31.
溢出了就会毫无规律的乱显示的机器
可接受的代码风格例子
EXAMPLES OF ACCEPTABLE CODING STYLE:
/*
* pow2plus1 - returns 2^x + 1, where 0 <= x <= 31
*/
int pow2plus1(int x) {
/* exploit ability of shifts to compute powers of 2 */
return (1 << x) + 1;
}
/*
* pow2plus4 - returns 2^x + 4, where 0 <= x <= 31
*/
int pow2plus4(int x) {
/* exploit ability of shifts to compute powers of 2 */
int result = (1 << x);
result += 4;
return result;
}
FLOATING POINT CODING RULES(浮点数编码规则)浮点数就不翻译了
For the problems that require you to implement floating-point operations,
the coding rules are less strict. You are allowed to use looping and
conditional control. You are allowed to use both ints and unsigneds.
You can use arbitrary integer and unsigned constants. You can use any arithmetic,
logical, or comparison operations on int or unsigned data.
没说不能用条件语句了
You are expressly forbidden to:
1. Define or use any macros.
2. Define any additional functions in this file.
3. Call any functions.
4. Use any form of casting.
5. Use any data type other than int or unsigned. This means that you
cannot use arrays, structs, or unions.
6. Use any floating point data types, operations, or constants.
NOTES:
1. Use the dlc (data lab checker) compiler (described in the handout) to
check the legality of your solutions.
2. Each function has a maximum number of operations (integer, logical,
or comparison) that you are allowed to use for your implementation
of the function. The max operator count is checked by dlc.
Note that assignment ('=') is not counted; you may use as many of
these as you want without penalty.
3. Use the btest test harness to check your functions for correctness.
4. Use the BDD checker to formally verify your functions
5. The maximum number of ops for each function is given in the
header comment for each function. If there are any inconsistencies
between the maximum ops in the writeup and in this file, consider
this file the authoritative source.
/*
* STEP 2: Modify the following functions according the coding rules.
*
* IMPORTANT. TO AVOID GRADING SURPRISES:
* 1. Use the dlc compiler to check that your solutions conform
* to the coding rules.
* 2. Use the BDD checker to formally verify that your solutions produce
* the correct answers.
*/
弄了一下午还没开始做题,搞环境和做记录这些事前准备就把时间耗完了。。。
三、环境配置-mac篇
人傻了,耽搁了还没开始做,就放寒假回家了,电脑只有mac,之前配的环境用不上了,我还没开始做就得再配置一个mac的
- 查了查其他的帖子,基本都推荐mac用docker进行安装linux环境来做
行吧,又得去学学docker的基本使用,这一层套一层,想配个环境还挺不容易的,不过之前听说docker是个容器,用处蛮大的,学了挺好的
结合上面的帖子,首先是下载docker,然后设置一下镜像,发现参考1的地址可以,参考2的不太行,所以此处用的是参考2的环境,但是参考1的镜像地址
再根据提示运行代码,就进入了实验环境了 以后每次开启docker的该环境后,运行该代码即可
docker exec -it datalab /bin/zsh
然后进入到目标目录下
试着编译一下,运行一下,这下成功了
可以选择在vscode里面编辑bits.c等文件,所以安装remote ssh,然后cmd+shift+p,呼出命令界面,键入remote ssh,选择add new host来跟该环境建立联系。
随后输入
ssh root@127.0.0.1 -p 9912
一路回车,连接的密码是THEPASSWORDYOUCREATED
然后就可以在vscode上编辑,并且在iterm2里进行编译运行btest,
注意每次做完bits.c的一道题后要运行的时候,要重新make,再运行
./btest -f functionName,比如第一道题的./btest -f bitXor,再运行./dlc bits.c来检测语法。
四、题解
1.bitXor
/*
* 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 2;
}
题意
题目的意思是得到异或的值,而不是x的y次方 比如例子中的4的二进制是100,5是101, 100 ^ 101 = 001 = 1
思路
步骤1
在做这题的时候,看了网上的一些解答,发现对逻辑运算这些已经忘记了,这个题可以用到德摩根定律来进行换算推出 参考1中写到,如下(注意下面的图片是错的,其实第一行右侧最上面少了一条横杠,这篇帖子的作者应该是写漏了,这句话我是在后面自己推导的时候发现的,然后补上括号的)
步骤2
上面的推导过程比较清晰,但是对我目前已经忘记大半来说,连这个符号都是猜的异或,所以便去查寻相关资料,参考2给出了答案
步骤3
可以看到 A⊕B 即是异或运算,且A⊕B=(A−B)∪(B−A)
示意图如下:
步骤4
回到步骤1中的式子,以下几行算是对第一行的右侧进行根据德摩根定律的变化,但是现在第一行右侧是如何得来的,而且AB表示的是什么,依旧忘记,所以翻书并总结如下
布尔运算有 非:~ , 与: & , 或: | , 异或: ^ 四种
这四种运算在不同的学科,有着不同的符号表示,比如概率论,或者其他学科,就有如下表示形式
∪ 或者 + 表示并集,即是 |运算,
∩ 或者 * 表示交集,即是 & 运算。
¬ 表示交集,即是 ~ 运算。
⊕ 表示交集,即是 ^ 运算。
AB看成 A ∩ B 即是 A & B ,画了一个总结图如下
那么后面的变换,到最后转换成 & 和 ~ 符号都能理解了,因此最后一个问题来了,第一行右侧怎么来的,为什么异或运算等于这个式子
步骤5
经查阅,根据真值表推出逻辑运算表达式这个方法可以做到,如下
没学过数电,但是也挺好理解的,写一下自己的理解:
因为真值表列出了所有的可能,那么这个目标逻辑的式子的真值,即是所有为1的情况的并集,因此是每种情况相加的结果;单看每一种为1的情况,既然是得到1,那么就是各个变量的交集(数学形式就是乘积)的结果为1,为了让0的情况不影响结果,就得给0加上非运算。
那么,对于异或运算来说:如下为真值表
| A | B | A^B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
则可以知2、3行为组成结果的因子,A^B的结果为真,只能是这两种情况之一,两种情况的并集即是结果。 所以可以得到如下,
A^B = (~A & B) | (A & ~B),则可以知道步骤1中的参考1里,第一行应该是两条大横杠表示两个非,他后续的推导也是两条横杠的推导,只不过写漏了。
推导过程
最后写出自己推导的过程,方便以后查看
答案
int bitXor(int x, int y) {
return ~(~x&~y)&~(x&y);
}
2.tmin
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void) {
return 2;
}
题意
得到补码形式的最小整形
只能用 ! ~ & ^ | + << >> 操作符
思路
看完书可以知道补码的最小值就是二进制的1000 0000 0000 0000 0000 0000 0000 0000
即是0x8000 0000,用这个是能通过btest的,但是不能通过dlc
这是由于题目给出了限制,这道题所用的常数不能超过8bit,所以要用移位符来让低位的常数,内部转换成最小值,比如 二进制的 1 ,直接左移31位,相当于直接加了31个0在1后面,就变成了32位的最小值。
答案
当然了,二进制10,即16进制0x2,往左移30位也是可以的
int tmin(void) {
return 0x2 << 30;
}
3.isTmax
/*
* 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 2;
}
题意
如果这个数是二进制补码编码的最大值,则返回1,否则返回0
思路
最大的补码是首位0,其他全是1,所以当验证的32位的数为
0111 1111 1111 1111 1111 1111 1111 1111
的时候,就应该返回1了,那么如何验证该数是max呢,观察max,发现max+1就是二进制补码的最小值,就是第二题的tmin可以检测了,但是此题不允许用移位符,不过一个思路是可以+1后验证是否是最小值,
max : 0111 1111 1111 1111 1111 1111 1111 1111
min : 1000 0000 0000 0000 0000 0000 0000 0000
那么 max = ~min ,按位取反嘛,同理 min = ~max
因为两个相同的数,异或的结果是0,只有不同的时候才是1,所以 min ^ (~max) 的结果应该是0,再取反就是目标答案了,所以答案是return !( (x+1) ^ (~x) )
但是少考虑了一种情况,当数x全为1的时候,(x+1) 因为溢出了,就全为0,然后!(x+1)^(~x)同样是相同的答案,但是却是错误的,因此,要用以上方法的话,需要排除全为1的这种情况
即当x全为1的时候,直接就返回0,x+1就是0, !!(x+1)就是0,直接返回这个就行
答案
int isTmax(int x) {
int tmin = x+1;
int equal = tmin ^ (~x);
return (!!tmin) & (!equal);
}
4.allOddBits
/*
* 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) {
return 2;
}
题意
如果x所有的偶数位上是1,则返回1,
比如 0xFFFFFFFD 是 1111 1111 1111 1111 1111 1111 1111 1101
偶数位上有0,则不返回1
0xAAAAAAAA是 1010 1010 1010 1010 1010 1010 1010 1010
偶数位上都是1,则返回1
思路
题目的例子0xAAAAAAAA已经给出了提示,这个数就是很完美的1010循环,只需要判断 x & 0xAAAAAAAA 是否还等于0xAAAAAAAA就行 所以直接可以写出
int allOddBits(int x) {
int temp = x&0xAAAAAAAA;
return temp == 0xAAAAAAAA;
}
可以得分,但是dlc过不了,因为要求只能用8bit,而且题目要求不能用==这个操作符
因此只需要完善这两处就行,8bit这个问题,可以有前面的题的经验,进行移位操作
int a=0xAA<<8; //0xAA00
int c=a|0xAA; //0xAAAA
int d=c<<16|c; //0xAAAAAAAA
等号的操作可以直接利用a == b 等价于 !((a & b)^b)
然后写成如下答案
//为什么会报错,是不是跟c语言的指针有关,
//a = a|0xAA;这个用一个新变量来声明就不报错
int a = 0xAA<<8;//0xAA00
a = a|0xAA;//0xAAAA
int d =(a << 16 ) | a;//该行dlc报错 parse error
return !( (x&d)^d );//该行dlc报错 undeclared variable `d'
结果发现btest能运行,但是dlc检测却报错了,多方求助后知道,在lab里要求所有声明的变量都得写在最前面,所以把变量写到前面就行,如下
答案
int allOddBits(int x) {
int a, d ;
a = 0xAA<<8;//0xAA00
a = a|0xAA;//0xAAAA
d =(a << 16 ) | a;//0xAAAAAAAA
return !( (x&d)^d );//相当于 == 的符号
}
5.negate
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x) {
return 2;
}
题意
输入x返回-x,感觉有点难啊,要考虑补码
思路
对于32位的补码来说 x + (~x) = 1111 1111 1111 1111 1111 1111 1111 1111
因为全1在补码里面就是-1,所以x + (~x) + 1 = 0 , 所以 x 与 (~x) + 1 互为相反数
记住结论 : -x = ~x + 1
答案
int negate(int x) {
return ~x + 1;
}
6.isAsciiDigit
/*
* 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 2;
}
题意
x在0x30~0x39区间的话,就返回1,否则返回0
思路
考虑同时满足条件 x - 0x30 >= 0 和 0x39 - x >= 0 就能证明在此区间
因为限制了不能用减法,上一道题知道了相反数的替代方式 -x = ~x + 1
所以 满足 x + (~0x30) + 1 >=0 和 0x39 + (~x) +1 >= 0 即可
问题是如何验证 >= 0,此时可以右移31位,留下符号位即可,当符号位为0是非负数,满足条件,所以要取反
只要写成 !((x + (~0x30) + 1)>>31) & !((0x39 + (~x) +1)>>31) 就行
答案
int isAsciiDigit(int x) {
return !((x + (~0x30) + 1)>>31) & !((0x39 + (~x) +1)>>31);
}
7.conditional
/*
* 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) {
return 2;
}
题意
实现三元运算符,x为真(1)就返回y,否则返回z
思路
两次逻辑非运算后,可以把x转为1或者0, 因为不能用if条件语句,所以要用位级运算来完成,可以用二进制的全0或者全1来操作
比如
- 0000 & y = 0000
- 1111 & y = y
为了得到 全0 和 全1 ,在补码里即是 0和-1,有如下
设 condition = !!x
condition的取值为 0或1
设 flag = ~condition + 1
flag的取值就变为 -1或0,得到了阶段性目标
设 ret_y = flag & y
设 ret_x = ~flag & x
当flag为0的时候,即0000,~flag为-1,即是1111
那么ret_y = 0000, ret_x = x
此时只需要return ret_y | ret_x = x
flag为1的时候正好相反,ret_y | ret_x = y
答案
int conditional(int x, int y, int z) {
int condition = !!x;
int flag = ~condition + 1;
int ret_y = flag & y;
int ret_z = ~flag & z;
return ret_y | ret_z;
}
8.isLessOrEqual
/*
* 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) {
return 2;
}
题意
如果x<=y 返回1,否则返回0
思路
y - x >= 0 跟之前那道题很像啊,不知道是不是符号又有另外的限制
判断y + (~x) + 1 >= 0 即可 直接右移31位,再取反
设置s_x = x>>31
y-x会发生溢出,然后就会发生错误,这就是与condition那道题不一样的地方
case 1 : x > 0 , y < 0 时 ;溢出会导致y-x>0,但实际上应该要<0,返回0
case_1 = (!s_x) & s_y ,判断这个就行,这个为1代表case_1满足,在不是这个的情况下,满足最上面的就行
case 2 : x < 0 , y > 0 时 ;溢出会导致y-x<0,但实际上应该要>0,返回1
case_2 = s_x & (!s_y) ,判断这个就行,这个为1代表case_2满足,然后就直接返回1
答案
int isLessOrEqual(int x, int y) {
int sign = y + (~x) +1;
int s_x = x>>31;
int s_y = y>>31;
int case_1 = (!s_x) & s_y ;
int case_2 = s_x & (!s_y);
return case_2|((!case_1)&(!(sign>>31)));
}
9.logicalNeg
/*
* 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 2;
}
题意
推出逻辑非操作符 !
思路
当全为0的时候,就返回1,其余的情况都返回0
x为0 的时候, -x也为0, 即是 ~x+1 = 0
x不为0 的时候,-x一定为正数或者负数,
那么二进制首位x为0/1,-x为1/0,完全相反,
所以x|-x,首位符号位必然为1,只有当x为0的时候 x|-x = 0
所以只需要移位符操作 移位31位就行
但是需要取反,因为当x=0的时候符号位为0,但是要求返回1,
当x!=0时,最后符号位为1,但是要求得到0,注意符号位为1,但是是补码编码,所以对应十进制是-1
所以直接+1就行
答案
int logicalNeg(int x) {
return ((x|(~x+1))>>31)+1;
}
10.howManyBits
/* 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) {
return 0;
}
题意
返回补码所需的最小位
思路
32位的数据,首先考虑非负数,如下
0000 0000 0000 0000 0000 0000 0000 0010
考虑从左至右,32-1位期间第一个不为0的数的位数,然后再+1就是答案,上行例子就是2+1=3
如何找到第一个不为0的数呢,此处用二分法,先考虑左右两个16位的部分
flag = !!(x >> 16) 得到左侧16位里,是否有1,然后记下计数标记
if flag = 1, bit_num > 16, cnt_16 = flag << 4 = 16
if flag = 0, bit_num < 16, cnt_16 = flag << 4 = 0
下一步 让x进行移位操作,比如如果上面的flag=1,那么,说明首位1在左侧16位里,下一次的查找就直接在这侧进行二分
那么就又需要对x进行移位了,要把x切成左侧的16位,所以算数右移16就行
同理,flag=0的时候,说明首位1在右侧16位里,不用算数右移,算数右移0位, 只需要二分的时候,右移8位,判断右侧16位的高8位就行,因为左侧16位全是0,不影响这8位的判断
x = x >> cnt_16
flag = !!(x >> 8)
if flag = 1,bit_num > 8, cnt_8 = flag << 3 = 8
if flag = 0,bit_num < 8, cnt_8 = flag << 3 = 0
下一步,同理,只是范围更小了
这个时候需要注意负数,用这个方法不适合,因为32位前面全是1,所以可以转换成正数来
32位表示范围是 -2^32 ~ 2^32 - 1
所以x转成正数, (~x)+1 ,然后再-1就是所需位数了,即是直接取反 ~x ,然后按照正数的方法来即可 比如-5,所需位数为4
1111 1111 1111 1111 1111 1111 1111 1011
直接取反,然后发现加上符号位就是4位
0000 0000 0000 0000 0000 0000 0000 0100
后面就需要把所有cnt加起来返回就行,
注意cnt_1的时候,x已经只有2位了,再二分后,就只判断一位了,不用在 flag>>0 了,
cnt_0的时候,x就只有一位了,就是本身,要么是0要么是1,所以cnt_0 = x,是0是1都没影响了
最后记得再加上一个符号位
答案
int howManyBits(int x) {
int flag,bit_num,
cnt_16,cnt_8,
cnt_4,cnt_2,
cnt_1,cnt_0,
sign;
sign = x>>31;//如果是1,说明是负数,要进行取反操作
x = (sign & (~x)) | (~sign & x);//sign是1,则是负数,1&x都是x,不改变,如果是0,那么0&x,都是0,就会看后面一个式子
flag = !!(x>>16);
cnt_16 = flag << 4;
x = x >> cnt_16;
bit_num = cnt_16;
flag = !!(x>>8);
cnt_8 = flag << 3;
x = x >> cnt_8;
bit_num += cnt_8;
flag = !!(x>>4);
cnt_4 = flag << 2;
x = x >> cnt_4;
bit_num += cnt_4;
flag = !!(x>>2);
cnt_2 = flag << 1;
x= x>>cnt_2;
bit_num += cnt_2;
flag =!!(x>>1);
cnt_1 = flag;
x = x>>cnt_1;
bit_num += cnt_1;
cnt_0 = x;
bit_num +=cnt_0;
return bit_num +1;
}
11.floatScale2
/*
* 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) {
return 2;
}
题意
思路
推导
答案
12.floatFloat2Int
/*
* 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) {
return 2;
}
题意
思路
推导
答案
13.floatPower2
/*
* 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) {
return 2;
}
题意
思路
推导
答案