【ICS计算机系统】位运算Lab1

这篇博客详细介绍了ICS计算机系统位运算的实验,包括与运算、获取字节、逻辑右移、比特计数等多个操作的实现原理和代码分析。涉及到补码、摩根定律、分治思想等概念,并探讨了不同位运算在处理负数和溢出问题时的策略。
摘要由CSDN通过智能技术生成

ICS-LAB1-Report


Bits.c

1. bitAnd–与

题目:

只用~和|实现&

样例:

bitAnd(6, 5) = 4

可使用操作: ~ |

最大操作数限制: 8

使用操作数: 4

int bitAnd(int x, int y) {
   
  return ~(~x | ~y); //De Morgan's laws
}

应用摩根律 ~(x | y) = ~x & ~y, 可得 x & y = (x | ~y)


2. getByte–获取字节

题目:

从x中提取字节n, n编号从0至3

样例:

getByte(0x12345678,1) = 0x56

可使用操作: ! ~ & ^ | + << >>

最大操作数限制: 6

使用操作数: 3

代码:

int getByte(int x, int n) {
   
  return (x >> (n << 3)) & 0xff;
}

分析:

由于 1Byte = 8bits = 2^3bits, 所以 n Bytes = 2^3 * n bits

因而将n左移3位,即 n * 2^3, 再将x右移 n * 2^3 即可将所求字节放在低8位,将其与上0xff,即可取出字节。


3. logicalShift–逻辑右移

题目:

将x逻辑右移n位

样例:

logicalShift(0x87654321,4) = 0x08765432

可使用操作: ! ~ & ^ | + << >>

最大操作数限制: 20

使用操作数: 10

代码:

int logicalShift(int x, int n) {
   
  //flag equals to: if n == 0 return 0; else return 1;
  int flag = !!n;
  int mask = ~(flag << (32 + (~n + 1)));
  return (x >> n) & mask;
}

分析:

  • 算数右移

算数右移即在右移后用原符号位数将高位补齐,保持右移后二进制数的符号保持不变。

  • 逻辑右移

逻辑右移即在右移后用 0 将高位补齐,是“逻辑上”的右移。

在正常右移运算中使用的是算数右移,因而要解决的问题即对于负数如何将最高位补上0,而非符号位1。
我采取掩码的方式,先将x正常右移n位与上其高位的掩码,使其右移产生的高位变为0

  • 掩码构造

掩码不能草率的构造为 ~(-1 << (32 - n)), 这种构造方式当n为0时会因-1被左移32位而导致异常,构造出来的mask仍为0

由于不能使用if,为判断n是否为0,我才用了一个flag = !n + ~0, 其有很好的性质。当n为0时,flag也为0,而当n不为零时,flag统一为-1,这样使用flag代替原先的-1, 从而避免上述情况。

这样我们可以使用 mask = ~(flag << (32 + (~n + 1))),来构造掩码,当n为0时,flag为0,从而mask = -1,避免上述错误。


4. bitCount–比特计数

题目:

返回二进制数中1的个数

样例:

bitCount(5) = 2, bitCount(7) = 3

可使用操作: ! ~ & ^ | + << >>

最大操作数限制: 40

使用操作数: 36

代码:

int bitCount(int x) {
   
  int tmp, l1, l2, l4, l8, l16; //tmp is used to save ops
  tmp = (0x55 << 8) + 0x55;
  l1 = (tmp << 16) + tmp; //0x55555555
  tmp = (0x33 << 8) + 0x33;
  l2 = (tmp << 16) + tmp; //0x33333333
  tmp = (0x0f << 8) + 0x0f;
  l4 = (tmp << 16) + tmp; //0x0f0f0f0f
  l8 = (0xff << 16) + 0xff; //0x00ff00ff
  l16 = (0xff << 8) + 0xff; //0x0000ffff

  x = (x & l1) + ((x >> 1) & l1);
  x = (x & l2) + ((x >> 2) & l2);
  x = (x & l4) + ((x >> 4) & l4);
  x = (x & l8) + ((x >> 8) & l8);
  x = (x & l16) + ((x >> 16) & l16);
  return x;
}

分析:

  • 分治思想

本题使用了一个简单的分治思想,对于一个二进制数,要对其中为1的位做计数, 对于1位二进制数来说,1的个数无非就是其本身所表示的1或0。利用这个特性,我们可以先将一个二进制数每一位独立分开为相间隔的两部分, 其每位表示的就是自身的二进制个数,再将两串二进制数对其相加,所得到的每两位分隔的二进制数就是表达这个位置的位为1的个数。

进一步相加为4位,8位其所代表的含义不变,最后合并至32位二进制数,其所表示的就是原二进制数中所含1的个数。

//以八位二进制数 10101110 为例//1|0|1|0|1|1|1|0 分割, 为两串1|1|1|10|0|1|0,再将其合并,成为 01 | 01 | <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值