计算机系统基础 实验——位运算

1.negate(取负)

要求:将所给数x取负。
操作:将操作数取反+1。

/* 
 * negate - return -x 
 *   Example: negate(1) = -1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 5
 *   Rating: 2
 */
int negate(int x) {
    
    
  return ~x+1;
}

2.bitOr(两数相或)

要求:只利用 ~ 和 & 操作,将数 x 和 y 相或。
操作
取或,等价于将两个数的取反值~x, ~y相与后,再取反。

/* 
 * bitOr - x|y using only ~ and & 
 *   Example: bitOr(6, 5) = 7
 *   Legal ops: ~ &
 *   Max ops: 8
 *   Rating: 1
 */
int bitOr(int x, int y) {
    
    
  return ~(~x&~y);
}

3.conditional(选择运算符)

要求:用逻辑运算实现三目运算:x ? y : z
思路
构造一个数 f,方便选择输出。
f = ~(!x) + 1; 当x为0时,f 的每一位全为1;当x不为0时,f 的每一位全为0

  • 取1的反(11111..110) +1 后,所有位都为1;
  • 取0的反(11111..111) +1 后,所有位都为0(溢出)。

之后选择输出y或z:return (z & f) + (y & ~f);

int conditional(int x, int y, int z) {
    
    
	int f = ~(!x)+1; //x为0,f每位全为1;x不为0,f每位全为0。 
	return (y & ~f) + (z & f); 
}

4.byteNot(对某个字节取反)

要求:将所给数 x 的第 n 个字节取反。
思路
取反可以用数位为1的数进行异或(^)操作,因为:

  • 原来数位为1,和1取异或后变为0;
  • 原来数位为0,和1取异或后变为1。

所以可以构造只有当前字节所在数位为1,其他位为0的数,和所给数相异或
一个字节占 8 个数位。
第 n 个字节,需要将数 0xff (11111111 = 255) 左移 8*n 个数位。
但是不能用乘法,所以可以将 8 表示为 2^3,构造数 y 为:y = n << 3
之后和 x 相异或,就把第n个字节取反了。

/* 
 * byteNot - bit-inversion to byte n from word x  
 *   Bytes numbered from 0 (LSB) to 3 (MSB)
 *   Examples: getByteNot(0x12345678,1) = 0x1234A978
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 6
 *   Rating: 2
 */
int byteNot(int x, int n) {
    
    
	int t = 0xff; //255:11111111 
	t <<= n<<3; //左移n*8位,变为00000..0011111111000..00 
	return x ^ t;
}

经验: 取反操作,异或上1。


5.allEvenBits

要求:判断数x的偶数数位是否都为1。
思路

方法1
int类型的数,偶数都为1,便是:01010101…0101。
所以先将数x的奇数数位全部变为0,然后判断其是否和上面的数相等就行了。

如何将数x的奇数数位全部变为0,偶数数位不变?
构造奇数数位全为0,偶数数位全为1的数y,和数x相与。

如何判断数x是否为 01010101…0101?
构造出该数,和数x相异或,看结果是否为0。若为0,则完全一致。

如何构造出奇数数位全为0,偶数数位全为1的数(01010101…0101)?
先构造出8位,再进行左移操作后相或。

int y=0x55;  y |= y<<8;  y |= y<<16;

方法2
构造奇数数位全为1,偶数数位全为0的数(10101010…1010),判断其和数 x 相或后是否所有数位全为1。如果是,则说明数 x 的偶数数位全为1。

如何判断是否一个数位全为1呢?
取反看其是否为0。或者直接判断其是否为11111(取^操作)

/* 
 * allEvenBits - return 1 if all even-numbered bits in word set to 1
 *   Examples allEvenBits(0xFFFFFFFE) = 0, allEvenBits(0x55555555) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 12
 *   Rating: 2
 */
int allEvenBits(int x) {
    
    
	//方法1:
	int y=0x55;
	y |= y<<8;
	y |= y<<16; //构造32位偶数位置全为1,奇数位置全为0的数:0101 0101... 
	x &= y; //先将x的奇数位上的值全部变为0 
	
	return !(x^y); //如果x^y=0的话,说明两个数完全一致,返回1;否则返回取!结果:0。
//	return x==y;

	//方法2:
	int y=0xAA;//,f=0xff;
	y = y|y<<8;
	y = y|y<<16;
	
//	f |= f<<8;
//	f |= f<<16; 
	
	return !~(x|y);
//	return !((x|y)^f);

6.byteSwap

要求:将数 x 的第 n 个字节和第 m 个字节互换。
思路
方法1:
将第 n 个字节化为 0,再和 “第 m 个字节” 相或。构造的 “第 m 个字节” 为:第 m 位不变,其他位为 0。
再将第 m 个字节化为 0,再和 “第 n 个字节” 相或。

方法2:
将第 n 个字节化为 1,再和 “第 m 个字节” 相与。 构造的 “第 m 个字节” 为:第 m 位不变,其他位为1。这个不好构造,需要 第m位 和 第m位为0,其他位为1的数 相或。
再将第 n 个字节化为1,再和 “第 n 个字节” 相与。

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

	int a = x>>(n<<3) & 0xff; //将第n个字节提出来
	int b = x>>(m<<3) & 0xff;
	
	
	//方法1:
	x &= ~(0xff<<(n<<3)); //第n个字节化为0
	x |= b<<(n<<3); //和第m位相或
	
	x &= ~(0xff<<(m<<3));
	x |= a<<(m<<3);
	
	//方法2:
	x |= 0xff<<(n<<3); //将第n个字节化为1
	x &= (~(0xff<<(n<<3)) | (b<<(n<<3))); //和第m位相与,注意这个需要其他数位为1
	
	x |= 0xff<<(m<<3);
	x &= (~(0xff<<(m<<3)) | (a<<(m<<3)));
	
	return x;
}

但是一开始想的做法不知道为什么不对:
将第n个字节上的值全部变为1,再和第m个字节上的值相与。
将第m个字节上的值全部变为1,再和第n个字节上的值相与。

一周后更——

注意:提出来的第n个字节经过移位到想要的位置,其他位置变为0了。所以在和其他数相与的时候需要特别注意,将其他位置变为1。有点麻烦。
所以尽量用这个移过位的数和其他数相或。


7.bitMask

要求:输出第 low 位到第 high 位为1,其余位置为0的数。若 low>high,返回0。
思路
构造数位全为1的数x。

  • 左移 low 位,使得第low位右边的全部数位变为0,记为 a。
  • 左移high+1位,使得第high位及其后面的全部数位变为0,记为 b。

那么,数 a 和数 b 仅仅 [low,high] 位不同,其余位相同。
可以将两个数异或,便得出 [low,high] 为1,其余为0的数了。

如何满足当 low>high 的时候,变为 0? 将最终答案再与上 a 即可。

/* 
 * bitMask - Generate a mask consisting of all 1's 
 *   lowbit and highbit
 *   Examples: bitMask(5,3) = 0x38
 *   Assume 0 <= lowbit <= 31, and 0 <= highbit <= 31
 *   If lowbit > highbit, then mask should be all 0's
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 16
 *   Rating: 3
 */
int bitMask(int highbit, int lowbit) {
    
    
	int x=~0;
	
	int a = x<<lowbit;
	int b = x<<highbit<<1;
	
	return (a ^ b) & a;
}

8.isLessOrEqual

要求:给出 x,y,判断是否 x ≤ y x≤y xy
思路

  • x 和 y 的符号位不同:如果 x 的符号位为 1 则满足;
  • x 和 y 的符号位相同:如果 y-x 的符号位为 0 则满足。

数 x 的符号位:最高位(第32位)。符号位为0为正数,为1为负数。

/* 
 * 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) {
    
    
	
	int a=(x>>31),b=(y>>31); //x和y的符号位
	int c=a+b; //a+b的符号位
	int d=(y+(~x)+1)>>31; //y-x的符号位
	
	return ((c&1)&a) | ((~c)&(!d));
	//如果x和y的符号位不同,c为1,第0位为1;否则c为0或2,第0位为0。
	//c为1时a也要为1,或者 c不为1且d为0。
}

但是有个疑问了,为什么定义 d=(x+(~y)-1)>>31 时,判断 (~c)&d 不行呢?
两周后更
哦对,如果用y-x的话,如果y和x相等的话,符号位一样为0,和x<y的符号位一致;但是用x-y的话,x和y相等时,符号位为0,和x<y时的符号位(1)不一致,还需要特判。所以用y-x更简洁些。


9.fitsShort

要求:判断一个32位二进制数 x 能否由16位二进制数表示。
思路
一个32位二进制数最后一位(第32位,从1开始数)为符号位,其余31位为有效位;一个16位二进制数最后一位(第16位)为符号位,其余15位为有效位。
如果一个32位二进制数可以由16位二进制数来表示的话,说明其符号位后面的所有位都是和符号位相同的。
也就说:如果这个数是正数的话,那么第16位到第32位需要都是0;如果是负数的话,都需要是1。
所以只需要判断这个数的最后17位是否全为0或者全为1便可。

/* 
 * fitsShort - return 1 if x can be represented as a 
 *   16-bit, two's complement integer.
 *   Examples: fitsShort(33000) = 0, fitsShort(-32768) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 8
 *   Rating: 1
 */
int fitsShort(int x) {
    
    
	x >>= 15; //右移15位,剩下符号位(变为16位时)和最后的16位。
	return !~x | !x; //判断剩下的17位是否符号相同,都为1或都为0.
}

如何判断是否所有位数都相同,都为0或都为1?
所有数位都为0好判断,即判断!x是否为1。
那么判断一个数都为1,只需要将这个数取反~x,判断取反之后是否为0便可。


10.fitsBits

要求:判断一个32位二进制数 x 能否由 n 位二进制数表示出来。
思路
和上一题思路相同,只需要判断剩下的 32-n 位是否和符号位相同,即判断第 n 位(从1开始数)以及后面的所有位数是否都相同。

/* 
 * fitsBits - return 1 if x can be represented as an 
 *  n-bit, two's complement integer.
 *   1 <= n <= 32
 *   Examples: fitsBits(5,3) = 0, fitsBits(-4,3) = 1
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */
int fitsBits(int x, int n) {
    
    
	x >>= (n + (~1+1)); //右移n-1位,除去有效位,判断符号位和其余位数是否都相同。
	return !x | !~x;
}

11.isTmin

要求:判断一个数 x 是否是 int 型最小值 tmin
思路
tmin 有性质tmin + tmin = 0. 所以 tmin = ~tmin+1.
但是要特判去掉同样满足的 0 [0x0]
判断相等用异或,异或值为0则相等。

/*
 * isTmin - returns 1 if x is the minimum, two's complement number,
 *     and 0 otherwise 
 *   Legal ops: ! ~ & ^ | +
 *   Max ops: 10
 *   Rating: 1
 */
int isTmin(int x) {
    
    
	return !( (x^(~x+1)) | (!x) );
}

判断 x 是否为 int 型最大值 tmax
tmax 有性质:tmax = ~(tmax+1)
但是要特判去掉同样满足的 -1[0xfffffff]

int isTmax(int x){
    
    
	return !( (x^(~(x+1)) | (!(~x)) )
}

经验

  • int 型最大值 tmax,最小值 tmin 性质:
    t m i n =   t m i n + 1 ; tmin = ~tmin+1; tmin= tmin+1;
    t m a x =   ( t m a x + 1 ) . tmax = ~(tmax+1). tmax= (tmax+1).
  • 判断相等用异或。

12.addOK

要求:判断两个数 int 型数 x,y 相加是否溢出。溢出返回0,否则返回1。
思路

  • 如果两个数符号不同,那么相加一定不会溢出。(因为两个数都是 int 范围内的,相加只会让一方抵消,绝对值不会增长)
  • 否则,如果两数之和的符号和这两个数的符号相同,就说明没有溢出。(溢出:正数+正数=负数,负数+负数=正数)

所以判断 x, yx+y 的符号便可。

/* 
 * addOK - Determine if can compute x+y without overflow
 *   Example: addOK(0x80000000,0x80000000) = 0,
 *            addOK(0x80000000,0x70000000) = 1, 
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 3
 */
int addOK(int x, int y) {
    
    
	/*
		判断两个数相加是否溢出。
		如果两数符号不同,相加不会溢出;
		否则,如果和的符号位和两数的符号相同,则不会溢出。 
	*/ 
	int a = x>>31; //x的符号位
	int b = y>>31; //y的符号位
	int c = (x+y)>>31; //x+y的符号位
	
	return ( (a^b)|!(c^a) )&1; //判断相等用异或
}

但是有个问题,为什么最后要加个 &1
给出的错误数据为:x=0x80000000, y=0x7ffffffff,返回的是-1。
但是在本地测试确实返回的1。
咋回事?


13.rotateLeft

要求:将 x 循环左移 n 位。循环左移:最高位移到最低位。
思路

  • 首先构造最后 n 位为1,其余位为 0 的掩码 mask。
  • 将其与 x 右移 32-n 所得的数 相与,于是得到 x 最左端的 n 位,并放置到了最右端。
  • x 左移 n 位,和 mask 相或,将最后 n 位补全。

就是,将最后 n 位取出,再放到前 n 位上。

如何构造最后 n 位为1,其余位为 0 的掩码 mask?
起初是想将全1右移32-n位,于是剩下最后n位在最右端,其余位为0。但是交上去不对。
错误之处:
全1表示为负数,而负数 右移 补位的是1,不是0;
但负数 左移 补位的却是0。

所以可以将全1左移n位,使得最后n位变为0。于是:

  • 可以直接将该数取反,变为最后n位为1,其余位为0的数;
  • 也可以仿照bitmask的做法:将该数与全1相异或,因为只有最后n位不同,所以最后n位变为1,其余位变为0;
  • 也可以将全1加上(1<<n),通过相加溢出,将第n位左边的所有位数变为0。
/* 
 *   rotateLeft - Rotate x to the left by n
 *   Can assume that 0 <= n <= 31
 *   Examples: rotateLeft(0x87654321,4) = 0x76543218
 *   Legal ops: ~ & ^ | + << >> !
 *   Max ops: 25
 *   Rating: 3 
 */
int rotateLeft(int x, int n) {
    
    
//	int mask = (~0)>>(32+(~n)+1); 错误,负数右移补1,不是0。 

	int a = ~0;
	int b = a<<n; //全1左移n位,使得最后n位为0。 
//	int mask = a^b; //a和b最后n位不同,异或,最后n位变为1,其余为0。 
	int mask = ~b; //取反 
	
//	int mask = (~0)+(1<<n); //巧用溢出
	mask &= (x>>(32+(~n)+1));
	
	x <<= n;
	x |= mask;
	
	return x;
}

经验
左移操作:丢弃高位,0补低位。
右移操作:正数右移高位补0,负数右移高位补1;无符号负数高位补0

易错点:
负数右移补1,左移补0。


14.isPower2

要求:判断一个数 x 是否是 2 的次幂数。
思路
如果是 2 的次幂数,那么 x 的二进制数只有一位是1,其余都是0。
那么 x-1 便是原来1位后面的所有位都是1。那么 x 和 x-1 相与便为0。
注意排除特殊情况:负数,0.

/*
 * isPower2 - returns 1 if x is a power of 2, and 0 otherwise
 *   Examples: isPower2(5) = 0, isPower2(8) = 1, isPower2(0) = 0
 *   Note that no negative number is a power of 2.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 4
 */
int isPower2(int x) {
    
    
	int y = x+(~1)+1;
	
	return !(x&y) & !(x>>31) & !!x;
}

15.sm2tc

要求:原码到补码的转换。给出原码表示x,返回其对应的补码表示。
思路
右移31位,将结果存为 t

  • 如果原数为正数,那么 t 此时全为0。而正数的补码不用改变;
  • 否则,t 全为1(负数右移补1)。负数补码,取反+1,符号位变为1。

所以可以将 x 或上 t,正数的话,不改变;负数就会取反。
是负数的话,最后要加上100…001,即(1<<31)+1。

int sm2tc(int x) {
    
    
	//原码到补码的转换 
	
	int t = x>>31; //t作为符号位
	return (x^t) + (((1<<31)+1)&t);
	
	//	int flag;
	//	if(!t) flag=0;
	//	else flag = ((1<<31)+1); //最低位最高位都变为1. 
	//	return (x^t) + flag;
}

剩下的两个浮点数运算就不更了,太难了。。T.T

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121013014