本次为一次计算机系统实验,就是使用一些基本的运算符来实现函数功能。

ps做这些题让我想起大一上学期刚学二进制时被鹏哥支配的痛苦。

知识准备:

1.负数等于正数取反加一。

2.左移一位相当于将这个数扩大两倍,右移两位相当于将这个数缩小两倍

3.符号位向右移动后,正数补0,负数补1。

4.负数补码最高位是1,正数补码最高位是0

5. 32位正数取值为

最大:01111111 11111111 11111111 11111111

最小:10000000 00000000 00000000 00000000

min:-2147483648  max:2147483647

1.

  1. /*
  2. * bitXor - 仅允许使用~和&来实现异或
  3. * 例子: bitXor(4, 5) = 1
  4. * 允许的操作符: ~ &
  5. * 最多操作符数目: 14
  6. * 分值: 1
  7. */

解题思路:简单的异或,a⊕b = (¬a ∧ b) ∨ (a ∧¬b)但要求使用&,所以需要德摩根律

  1. int bitXor(int x,int y)
  2. {
  3. return ~(~(~x&y)&~(x&~y));
  4. }

但这样还不是最简单的,如果使用同或的非来表示异或,a⊕b = ¬((a ∧ b) ∨ (¬a ∧¬b))还能再少用一个字符。

  1. int bitXor(int x, int y)
  2. {
  3. return ~(x&y)&~(~x&~y);//同或的非
  4. }

2.

  1. /*
  2. * tmin - 返回最小的二进制补码
  3. * 允许的操作符: ! ~ & ^ | + << >>
  4. * 最多操作符数目: 4
  5. * 分值: 1
  6. */

解题思路:最小值为0x8000 0000,我们可以将1左移31位得到最小值。

  1. int tmin(void)
  2. {
  3. return 1<<31;
  4. }

3.

  1. /*
  2. * isTmax - 如果x是最大的二进制补码,返回1;否则,返回0
  3. * 允许的操作符: ! ~ & ^ | +
  4. * 最多操作符数目: 10
  5. * 分值: 2
  6. */

解题思路:最大的二进制补码为0x7FFFFFFF,为判断输入是否为这一个数,我们只需要将其与最小的二进制补码与或一下判断是否为0即可。

  1. int isTmax(int x)
  2. {
  3. return !(x^~(1<<31));
  4. }

4.

  1. /*
  2. * negate - 返回-x
  3. * 例子: negate(1) = -1.
  4. * 允许的操作符: ! ~ & ^ | + << >>
  5. * 最多操作符数目: 5
  6. * 分值: 2
  7. */

解题思路:正数取反加一即为负数。

  1. int negate(int x)
  2. {
  3. return (~x+1);
  4. }

5.

  1. /*
  2. * allOddBits - 如果所有奇数位都为1则返回1;否则返回0
  3. * 例子: allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
  4. * 允许的操作符: ! ~ & ^ | + << >>
  5. * 最多操作符数目: 12
  6. * 分值: 2
  7. */

解题思路::只有所有奇数位为1的数,与0x5555 5555进行&运算才会得到0。5->(0101)。故而需要得到0x5555 5555,将0x55(01010101)分别左移8、16、24得到3个数,然后将这三个数相加即可得到0x5555 5555。

  1. int allOddBits(int x)
  2. {
  3. return !(~(x|(85+(85<<8)+(85<<16)+(85<<24))));
  4. }

6.

  1. /*
  2. * isAsciiDigit - 如果x是ascii码中的0~9,返回1;否则返回0
  3. * 例子: isAsciiDigit(0x35) = 1.
  4. * isAsciiDigit(0x3a) = 0.
  5. * isAsciiDigit(0x05) = 0.
  6. * 允许的操作符: ! ~ & ^ | + << >>
  7. * 最多操作符数目: 15
  8. * 分值: 3
  9. /*

解题思路:若x是数字,则x在‘0’~‘9’之间。可以用x-48>=0和x-58<0(x+~48+1>=0和x+~58+1<0)来计算。

  1. int isAsciiDigit(int x)
  2. {
  3. return !((x+~48+1)>>31)&!!((x+~58+1)>>31);
  4. }

7.

  1. /*
  2. * isLessOrEqual - 如果x<=y返回1否则返回0
  3. * 例子: isLessOrEqual(4,5) = 1.
  4. * 允许的操作符: ! ~ & ^ | + << >>
  5. * 最多操作符数目: 24
  6. * 分值: 3
  7. */

解题思路:

直接用y-x可能会超出int的表示范围,故而:当x与y同号时,转换为p=y-x>=0,然后p符号位(p>>31)&1为0则返回1,符号位1则返回0;异号时,只要x>=0,就要返回0,否则返回1,由(x>>31)&1能达到该效果。c=a(x>>31)+b(y>>31)可作为x,y同号异号的判断,异号的时候为1和0,相加为1;同号的时候为1和1或者0和0,相加得0或2,取反之后必为非0。

  1. int isLessOrEqual(int x, int y)
  2. {
  3. int a=x>>31;
  4. int b=y>>31;
  5. int c=a+b;
  6. int p=y+(~x+1);///p=y-x
  7. int q=!((p>>31)&1);///若p>=0,则q为1,成立;反之,q为0,不成立。
  8. return (c&(a&1))|((~c)&q);///若为异号,c为1,若a为1,说明x<=y,成立;反之说明x>y不成立。
  9. ///若为同号,c为0或者2,取反后为非0,若q为1,相与后必为非0。
  10.  
  11. }

8.

  1. /*
  2. * conditional - 实现x?y:z
  3. * 例子: conditional(2,4,5) = 4
  4. * 允许的操作符: ! ~ & ^ | + << >>
  5. * 最多操作符数目: 16
  6. * 分值: 3
  7. */

解题思路:当x不为0时,!x=0,函数结果为y。这时候需要(0xffff ffff)&y|(0x0),来保存y。 当x为0时,!x=1,函数结果为z。这时候需要(0x0)|(0xffff ffff)&z,来保存z。

  1. int conditional(int x, int y, int z)
  2. {
  3. return ((!x+~1+1)&y)|((~!x+1)&z);
  4. }

9.

  1. /*
  2. * absVal - absolute value of x
  3. * Example: absVal(-1) = 1.
  4. * You may assume -TMax <= x <= TMax
  5. * Legal ops: ! ~ & ^ | + << >>
  6. * Max ops: 10
  7. * Rating: 4
  8. */

解题思路:对整数取绝对值,我们知道int类型的实数,正负数的区别在于最高位是0还是1,正数最高位是0,负数最高位是1。正数右移31位后为0,负数右移31位后为-1(负数右移高位补1)。而负数要变为正数,我们知道需要-1再取反。那么我们就可以利用这两个性质来做题了。

/*

正数右移31位后为0,负数右移31位后为-1(负数右移高位补1)
异或1取反
异或0不变
负数要变为正数需要+1之后取反

*/

  1. int absVal(int x)
  2. {
  3. int t = (x>>31);
  4. return (x+t)^t;
  5. /*正数右移31位后为0,负数右移31位后为-1(负数右移高位补1)*/
  6. }

10.

  1. /*
  2. * bitCount - returns count of number of 1's in word
  3. * Examples: bitCount(5) = 2, bitCount(7) = 3
  4. * Legal ops: ! ~ & ^ | + << >>
  5. * Max ops: 40
  6. * Rating: 4
  7. */

解题思路:这题应该是这次作业里最难的题了,只用40次操作,并且不能用循环和if,计算x二进制表示中1的个数。首先需要设计5个常数,分别为0x55555555,0x33333333,0x0f0f0f0f,0x00ff00ff,0x0000ffff。

为什么这么设计呢,可以把5个数字的二进制写出来,分别是间隔1个0,2个0,4个0,8个0,16个0

然后下面的计算方法,和间隔对应,分别右移1,2,4,8,16次。如何理解呢,如果只给你一个只有2位的二进制数x,以及01,怎么计算里面1的个数呢,是不是做(x&1)+((x>>1)&1)呢?通过移位,把高位的1,移到低位求和。32位的数字就可以看成是16个2位的,之后就可以等价看作,一个16位的数字,分成8段,做上面第二个计算操作。总的来看这个操作就是把高位的1往低位移动,然后类似分治,分成多段的移动。非常nice的一个题哦。关于这道题的一些理解,32位数字看成16个2位的,两个相邻位置作为一组进行一次计算统计1的个数,然后进行合并;再之后四个相邻位置作为一组进行一次计算统计1的个数;之后八个,十六个,三十二个作为一组。

  1. int bitCount(int x)
  2. {
  3.  
  4. int mask1 = 0x55;
  5. int mask2 = 0x33;
  6. int mask3 = 0x0f;
  7. int mask4 = 0xff;
  8. int mask5 = 0xff;
  9. mask1 += mask1<<8;
  10. mask1 += mask1<<16;//generate 0x55555555
  11. mask2 += mask2<<8;
  12. mask2 += mask2<<16;//generate 0x33333333
  13. mask3 += mask3<<8;
  14. mask3 += mask3<<16;//generate 0x0f0f0f0f
  15. mask4 += mask4<<16;//generate 0x00ff00ff
  16. mask5 += mask5<<8; //generate 0x0000ffff
  17. x =((x>>1)&mask1) + (x&mask1);
  18. x =((x>>2)&mask2) + (x&mask2);
  19. x =((x>>4)&mask3) + (x&mask3);
  20. x =((x>>8)&mask4) + (x&mask4);
  21. x =((x>>16)&mask5) + (x&mask5);
  22. return x;
  23. }

11.

  1. /*
  2. * byteSwap - swaps the nth byte and the mth byte
  3. * Examples: byteSwap(0x12345678, 1, 3) = 0x56341278
  4. * byteSwap(0xDEADBEEF, 0, 2) = 0xDEEFBEAD
  5. * You may assume that 0 <= n <= 3, 0 <= m <= 3
  6. * Legal ops: ! ~ & ^ | + << >>
  7. * Max ops: 25
  8. * Rating: 2
  9. */

解题思路:需要我们将字节交换,主要这里交换的是字节而不是位!我们知道int型有4个字节,每个字节有8位,这里我们就以8位为一组组成字节进行整体交换。思路很简单,我们先将需要交换的字节转移到交换后的位置,在这个过程中不可避免的会夹带着其他的字节的变化,先不去管,再获得一个排除需要交换字节的原数(需要交换的字节的位置全是0),最后三者一与便得到交换后的数了。

  1. int byteSwap(int x, int n, int m)
  2. {
  3.  
  4. int a = n<<3;
  5. int b = m<<3;//乘以8,因为8位一个字节,投射到该字节对应的位上
  6. int step1 = ((x>>a)&0xff)<<b;
  7. int step2 = ((x>>b)&0xff)<<a;
  8. // int step3 = x&~(0xff<<b)&~(0xff<<a);
  9. int step3 = x&(~((0xff<<a)|(0xff<<b)));
  10. return (step1|step2|step3);//将需要交换的字节扣除来,错位交换
  11. }
  1. /*
  2. * oddBits - return word with all odd-numbered bits set to 1
  3. * Legal ops: ! ~ & ^ | + << >>
  4. * Max ops: 8
  5. * Rating: 2
  6. */

解题思路:得到int型32位上一个奇数位全是1的数,那么就是0xaaaaaaaa(1010),那么最简单的方法就是通过0x55构造就可以了。注意这里的第0位安排不是奇数位,安排到了偶数位上了。

  1. int oddBits(void)
  2. {
  3. int a=0xaa;//1010 1010
  4. a=a|(a<<);
  5. a=a|(a<<);
  6. return a;
  7. // return 0xaaaaaaaa;?
  8. }

12.

  1. /*
  2. * isLess - if x < y then return 1, else return 0
  3. * Example: isLess(4,5) = 1.
  4. * Legal ops: ! ~ & ^ | + << >>
  5. * Max ops: 24
  6. * Rating: 3
  7. */

解题思路:一开始的思路就是用x-y看看得到的最高位是1还是0,是1则是负数说明x<y,是0则是正数说明x>y,而且得到的最高位恰好是返回值。但是有这么一个问题,可能会出现溢出啊,int类型存不下!我在这里停了好久,也试着用long long 来存数,但是只能使用int类型来进行运算,不能使用其他类型的数,不能进行强制类型的转化。

其实我们可以把那些会造成溢出的数给分离出来,我们知道正数减负数和负数减正数才可能会造成溢出,也就是说只有x和y异号的时候才可能出现溢出。而如果没有溢出的可能,则x-y的最高位便必然是所求的结果。那么就来模拟一个if判断便可以了。这里将flag=x^y作为判断项,如果flag=1说明可能会溢出,那么如果是负数-正数返回1,如果是正数-负数返回0:如果flag=0则不会产生溢出了,就用x-y来判断。

/* 可能会造成溢出的情况:
负数-正数 1
正数-负数 0
(异号)

一定不会造成溢出:
正数-正数
负数-负数
(同号)
x-y
*/

  1. int isLess(int x, int y)
  2. {
  3. int t = x+~y+1;///x-y
  4. int flag = x^y;
  5. return (((flag&x)|(t&~flag))>>31)&1;
  6. }

13.

  1. /*
  2. * satAdd - adds two numbers but when positive overflow occurs, returns
  3. * maximum possible value, and when negative overflow occurs,
  4. * it returns minimum positive value.
  5. * Examples: satAdd(0x40000000,0x40000000) = 0x7fffffff
  6. * satAdd(0x80000000,0xffffffff) = 0x80000000
  7. * Legal ops: ! ~ & ^ | + << >>
  8. * Max ops: 30
  9. * Rating: 4
  10. */

解题思路:题意要求如果两个数相加,如果产生了正溢出返回0x7fffffff,出现负溢出返回0x80000000,其他情况返回两数之和。

这里我的思路很简单就是使用位运算来模拟一下if判断,有点数据选择器的意思?开始就先构造0x80000000和0x7fffffff这两数数恰好构造出一个另一个取反就可以了。产生溢出只有两种情况正数加正数溢出产生负数,负数加负数溢出产生正数,所以根据这一特点就可以判断是否产生溢出,在根据两数相加得到的数的正负就可以继续判断正溢出和负溢出了。

/*
产生溢出:
1.正数+正数=负数 正溢出 0x7fffffff
2.负数+负数=正数 负溢出 0x80000000

不溢出:
x+y

*/

  1. int satAdd(int x, int y)
  2. {
  3. int ans = x+y;
  4. int over = (x^ans)&(y^ans);
  5. int min_ans = <<;///0x80000000
  6. int max_ans = ~(min_ans);//0x7fffffff
  7. int neg_over = ((over&x)>>)&;
  8. int pos_over = ((over&~x)>>)&;
  9. return ((~pos_over+)&max_ans)+((~neg_over+)&min_ans)+((~(!(neg_over|pos_over))+)&ans);
  10. }

14.

  1. /*
  2. * float_twice - Return bit-level equivalent of expression 2*f for
  3. * floating point argument f.
  4. * Both the argument and result are passed as unsigned int's, but
  5. * they are to be interpreted as the bit-level representation of
  6. * single-precision floating point values.
  7. * When argument is NaN, return argument
  8. * Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
  9. * Max ops: 30
  10. * Rating: 4
  11. */

先插入一个怎么把十进制小数转换成二进制的小数的方法---->

解题思路:将一个浮点数扩大两倍。首先如果参数是非数NaN就返回参数本身,那么就先将这一种情况排除掉。对于大多数浮点数阶码不为0,那么阶码加1便可以加倍。对于那些阶码为0的,若是尾数不为0,尾数左移1位便可以实现加倍了;若也尾数为0,那么这个数就是0了,返回原来的0即可。

开始我在这里有一个疑惑,尾数左移一位要是进入了阶码的范围,还对吗?其实这也是正确的,因为若是尾数能够进位,那么阶码加1,整个浮点数值确实是加倍了。

同时这里还要说明一下,尾数是用23位数来表示24位尾数,基数是2,需要规格化尾数形式为+-0.1bb...b,其中第一位“1”不明显表示出来。

举个例子:

-5.625 = C 0 B 4 0 0 0 0

C 0 B 4 0 0 0 0 1100 0000 1011 0100 0000 0000 0000 0000 按照浮点数格式切割成相应的域 1 1000 0001 01101 000000000000000000 经分析:符号域1 意味着负数;指数域为129 意味着实际的指数为2 (减去偏差值127); 尾数域为01101 意味着实际的二进制尾数为1.01101 (加上隐含的小数点前面的1)。

所以,实际的实数为:

= -1.01101 × 2^2

=- ( 1*2^0 + 1*2^(-2) + 1*2^(-3) + 1*2^(-5) ) × 2^2

= -(1+0.25+0.125+0.03125)*4

= -1.40625*4

= -5.625

  1. unsigned float_twice(unsigned uf)
  2. {
  3. unsigned S = uf&0x80000000;///获得第0位的符号位
  4. unsigned Exp = uf&0x7f800000;///获得第1~8位的阶码
  5. unsigned M = uf&0x007fffff;///获得第9~31的尾数
  6. unsigned T = uf&0x7fffffff;
  7. unsigned ans = uf;
  8. if(T >= 0x7f800000)///如果是NaN的情况
  9. {
  10. return uf;
  11. }
  12. if(Exp!=)///阶码不为0,那想要翻倍就只需要阶码加1即可。
  13. {
  14. ans = uf + 0x00800000;
  15. // return S+Exp+0x00800000+M;
  16. }
  17. else///阶码为0,那就得通过变换尾数来加倍
  18. {
  19. if(M!=)///尾数左移一位加倍
  20. {
  21. ans = (uf<<) + S;
  22. // return S+E +(M<<1);
  23. }
  24. }
  25. return ans;
  26. }

/*
float中
第0位为符号位
第1~8位为阶码
第9~31位为24位二进制原码小数表示的尾数
举个例子:
(65878)10进制
= (1 0000 0001 0000 0110)2进制
= (0.1000 0000 1000 0011 0)2进制*2^17
符号S =0
阶码E =(128+17)10进制
= (145)10进制
= (1001 0001)二进制
所以浮点型表示为
0 1001 0001 000 0000 1000 0011 0000 0000

*/

CSAPP lab1——位运算的更多相关文章

  1. 关于C/C++中的位运算技巧

    本篇文章讲述在学习CSAPP位运算LAB时的一些心得. 移位运算的小技巧 C/C++对于移位运算具有不同的策略,对于无符号数,左右移位为逻辑移位,也就是直接移位:对于有符号数,采用算术移位的方式,即左 ...

  2. CSAPP:Lab1 -DataLab 超详解

    写在前面 之前考研的时候csapp的书有刷过5,6遍,所以对书本知识还算比较了解.恰逢最近在学c++的时候,顺带刷一下大名鼎鼎的csapp实验. 0. 环境准备 最好准备一个纯净的Linux系统这里建 ...

  3. Java 位运算2-LeetCode 201 Bitwise AND of Numbers Range

    在Java位运算总结-leetcode题目博文中总结了Java提供的按位运算操作符,今天又碰到LeetCode中一道按位操作的题目 Given a range [m, n] where 0 <= ...

  4. 简简单单学会C#位运算

    一.理解位运算 要学会位运算,首先要清楚什么是位运算?程序中的所有内容在计算机内存中都是以二进制的形式储存的(即:0或1),位运算就是直接对在内存中的二进制数的每位进行运算操作 二.理解数字进制 上面 ...

  5. SQL Server时间粒度系列----第8节位运算以及设置日历数据表节假日标志详解

    本文目录列表: 1.位运算 2.设置日历数据表节假日标志 3.总结语 4.参考清单列表   位运算   SQL Server支持的按位运算符有三个,分别为:按位与(&).按位或(|).按位异或 ...

  6. js中的位运算

    按位运算符是把操作数看作一系列单独的位,而不是一个数字值.所以在这之前,不得不提到什么是"位": 数值或字符在内存内都是被存储为0和 1的序列,每个0和1被称之为1个位,比如说10 ...

  7. Java中的位运算

    昨天去面试的时候做到了一道Java的位运算题目,发现有个运算符不懂:">>>",今天特地查了一下,并小结一下常见的位运算符号: ~  按位非(NOT)(一元运算) ...

  8. C#位运算讲解与示例

    首先每一个权限数都是2的N次方数 如:k1=2 ; //添加 k2=4 ; //删除 k3=8; //修改 ... 如此定义功能权限数,当需要组合权限时,就需要对各个所拥有的权限数按位或了. 如: p ...

  9. C#枚举中的位运算权限分配浅谈

    常用的位运算主要有与(&), 或(|)和非(~), 比如: 1 & 0 = 0, 1 | 0 = 1, ~1 = 0 在设计权限时, 我们可以把权限管理操作转换为C#位运算来处理. 第 ...

随机推荐

  1. LCT能干啥???

    LCT能干啥       模板:   维护可加的树链信息:询问都是一条链上的信息:维护方式和线段树差不多: 增加一条边: 删除一条边: 修改一个点权: 修改一条路径上的所有点的点权:     整体来说 ...

  2. SQL- SQL查询检索阶段一

    一 说明 如果是初学者,建议去网上寻找安装Mysql的文章安装,以及使用navicat连接数据库,以后的示例基本是使用mysql数据库管理系统: 二 准备前提 需要建立一张学生表,列分别是id,名称, ...

  3. c++之指针

    一.指针的基本概念 指针的作用:可以通过指针间接访问内存. 内存编号是从0开始记录的,一般用十六进制数字表示. 可以利用指针变量保存地址. 二.指针变量的定义和使用 指针变量定义语法:数据类型 *变量 ...

  4. HttpRunner学习9--切换测试报告模板

    前言 在HttpRunner中,给我们提供了 2 套测试报告模板,分别是 default_report_template.html 和 extent_report_template.html . 默认 ...

  5. Vsftpd运行的两种模式-xinetd运行模式和 standalone模式

    vsftpd运行的两种模式-xinetd运行模式和 standalone模式 vsftpd提供了standalone和inetd(inetd或xinetd)两种运行模式. standalone一次性启 ...

  6. JS---封装getScroll函数 & 案例:固定导航栏

    封装getScroll函数 1. 获取页面向上或者向左卷曲出去的距离的值 2. 浏览器的滚动事件 function getScroll() { return { left: window.pageXO ...

  7. Codeforces Round #608 (Div. 2) 题解

    目录 Codeforces Round #608 (Div. 2) 题解 前言 A. Suits 题意 做法 程序 B. Blocks 题意 做法 程序 C. Shawarma Tent 题意 做法 ...

  8. 面试连环炮系列(六):Dubbo应用为什么要部署Zookeeper

    Dubbo应用为什么要部署Zookeeper? Zookeeper用来注册和发现服务,简单说就是提供端注册接口信息到Zookeeper,调用端在Zookeeper上查找接口对应的服务IP和端口.由于Z ...

  9. pctfree和pctused

    pctfree 是指一个数据块保留的空间百分比,表示数据块在什么情况下可以被insert 默认是10,表示当数据块的可用空间低于10%后,就不可以被insert了,只能被用于update了 即:当使用 ...

  10. Dynamic Code Evaluation:Code Injection 动态代码评估:代码注入