Datalab 实验报告
以下能简化的操作符个数都已经尽量最简单了,欢迎在评论区提供新的思路和方法。是学校的Lab,不要抄袭可以借鉴~
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) {
int X = x & y;
int Y = ~x & ~y;
return ~X & ~Y ;
}
Sol 利用真值表法得到x^y=(x&(~y)) | (~x&y) 其中 | 根据摩尔根定律需要使用三个操作符,再利用主合取范式和主析取范式等值得到异或表示
2.evenBits
- evenBits - return word with all even-numbered bits set to 1
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 8
- Rating: 1
int evenBits(void) {
int x=0x55;
x=x|(x<<8);
x=x|(x<<16);
return x;
}
Sol 0x55是低8位上偶数位全为1,然后利用并行思想即可
3.fitsShort
- 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) {
return ! (((x<<16)>>16) ^x );
}
Sol 推导得知,如果可以被表示为16位数即前17位全是0或全是1,即x右移15位后全是0或全是1
4.isTmax
- isTmax - returns 1 if x is the maximum, two’s complementc number,and 0 otherwise
- Legal ops: ! ~ & ^ | +
- Max ops: 10
- Rating: 1
int isTmax(int x) {
int p=x+1;
return ! ( (p+p) | !p);
}
Sol 即判断x是否为0x7fffffff,由于Tmax+1=Tmin 即判断x+1是否为Tmin即可。判断方法即让其溢出为0。此时若x=-1也满足条件,故只需排除x=-1的情况即可
5.fitsBits
- 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) {
n=n+31;
return ! (((x>>n)+1) >>1);
}
Sol 由(3)得知,判断某个数是否能用n位补码完全表示即该数右移n-1位后是否全是0或全是1. 需要利用并行思想将全是0和全是1的情况一起处理,即统一+1后得到只会是1和0。 优化时利用移动位数自动模32的原理: n-1 ≡ \equiv ≡ 31+n (mod 32)
6.upperBits
- upperBits - pads n upper bits with 1’s
- You may assume 0 <= n <= 32
- Example: upperBits(4) = 0xF0000000
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 10
- Rating: 1
int upperBits(int n) {
int x=(!!n)<<31;
return x>>(31+n);
}
Sol 需要将n=0和n$\not = 0 的 情 况 并 行 考 虑 , n = 0 返 回 0 , n 0的情况并行考虑,n=0返回0,n 0的情况并行考虑,n=0返回0,n\not =$0返回(1<<31)>>(n-1),优化思路同(5)
Tips 0和非0可以使用!!来统一
7.allOddBits
- allOddBits - return 1 if all odd-numbered bits in word set to 1
- Examples allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 12
- Rating: 2
int allOddBits(int x) {
int y=0xAA;
y=y|(y<<8);
y=y|(y<<16);
return !((x&y)^y);
}
Sol 利用(2)得到奇数位上全为1的数,只需判断y ∈ \in ∈x即可
8.byteSwap
- 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) {
n=n<<3;
m=m<<3;
int a=0xff;
int p=( (x>>m) ^ (x>>n) ) & a;
return x^(p<<m)^(p<<n);
}
Sol 首先将位转化为字节即*8;然后利用swap的核心思想
将需要换位的数转移到同一字节上即可
9.absVal
- absVal - absolute value of x
- Example: absVal(-1) = 1.
- You may assume -TMax <= x <= TMax
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 10
- Rating: 4
int absVal(int x) {
int p=x>>31;
return p^(x+p);
}
Sol 当x为负数时,x+1即为x对应的绝对值。需要将负数与正数一起考虑,考虑到x+1=~(x-1)=(x-1)^(-1),刚好对应x的符号位右移31位
Tips -1和0可以使用算数右移来统一
10.divpwr2
- divpwr2 - Compute x/(2^n), for 0 <= n <= 30
- Round toward zero
- Examples: divpwr2(15,1) = 7, divpwr2(-33,4) = -2
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 15
- Rating: 2
int divpwr2(int x, int n) {
int p=x>>31;
return (x+ ((p<<n) ^ p ) ) >>n;
}
Sol 考虑到负数时的右移为向上取整,正数时的左移为向下取整,故只需给负数加上偏移值使其变为向下取整即可
11.leastBitPos
- leastBitPos - return a mask that marks the position of the least significant 1 bit. If x == 0, return 0
- Example: leastBitPos(96) = 0x20
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 6
- Rating: 2
int leastBitPos(int x) {
return (~x+1)&x;
}
Sol 不难推出
x
&
(
x\&(
x&(~x+1)会得到x的最低有效位考虑树状数组lowbit函数即可
12.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 ((x| (~x+1)) >>31 ) + 1;
}
**Sol ** 考虑到0和正数的符号位均为0,负数的符号位为1,同时由于需要将任何整数化为0/1,故需要将数字转为-1,0,1中的某一个才能用逻辑运算得到0和1
考虑到任何非零数, x & ( x \& ( x&(~ x + 1 ) x+1) x+1)一定会保留最低有效位的1,同时 x ∣ ( x|( x∣(~ x + 1 ) x+1) x+1)一定包含$x KaTeX parse error: Expected 'EOF', got '&' at position 1: &̲(KaTeX parse error: Can't use function '\~' in math mode at position 1: \̲~̲x+1)$ ,故可推出 x ∣ ( x|( x∣(~ x + 1 ) x+1) x+1)一定会将最高位变成1
13.bitMask
- 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 H=highbit,L=lowbit;
int x=~0;
x= ((x<<L)&(~((x<<H)<<1)));
return x;
}
Sol 唯一难点是H=31时不能移动直接32位否则会自动取模
Tips 左移31位再左移1位 ≠ \not = = 左移32位
14.isLess
- isLess - if x < y then return 1, else return 0
- Example: isLess(4,5) = 1.
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 24
- Rating: 3
int isLess(int x, int y) {
int p=~y;
return (((x&p) | (x^p) & (x+p+1) ) >> 31) & 1;
}
Sol 判断 x x x和 y y y是否异号,同号再判断差值符号位即可,利用等值演算后可减少部分操作符
15.logicalShift
- logicalShift - shift x to the right by n, using a logical shift
- Can assume that 0 <= n <= 31
- Examples: logicalShift(0x87654321,4) = 0x08765432
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 20
- Rating: 3
int logicalShift(int x, int n) {
int y=1<<31;
y=y>>n;
y=y<<1;
return (x>>n)&(~y);
}
Sol 即得到一个最高n位全为0后些位全为1的掩码即可
16.satMul2
- satMul2 - multiplies by 2, saturating to Tmin or Tmax if overflow
- Examples: satMul2(0x30000000) = 0x60000000
- satMul2(0x40000000) = 0x7FFFFFFF (saturate to TMax)
- satMul2(0x60000000) = 0x80000000 (saturate to TMin)
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 20
- Rating: 3
int satMul2(int x) {
int t=x<<1;
int p=t>>31;
int q=(x^p)>>31;
int Tmin=1<<31;
int T=Tmin^p;
return ((~q)&t) + (q&T) ;
}
Sol 不难得知溢出情况只有第31位和第30位异号时才会出现。当同号时直接左移一位即可,当异号时,负数乘2溢出希望还是负数即Tmin正数乘2溢出还是正数即Tmax,观察到 T m i n + ( − 1 ) = T m a x Tmin+(-1)=Tmax Tmin+(−1)=Tmax
但是这种情况在平台上报错了(?好像是溢出出现了ud然后被自动优化错了
于是考虑 T m i n Tmin Tmin^ ( − 1 ) = T m a x (-1)=Tmax (−1)=Tmax
利用溢出时一定最高两位一定会异号的前提即可将正数负数一同考虑了
优化 巧妙的利用掩码和aba=b的性质对上述答案进行缩减即可
int satMul2(int x) {
int t=x<<1;
int p=x>>31;
int q=t>>31;
int Ok=p^q;
int Tmin=1<<31;
int T=(Tmin^q^t)&ok;
return T^t;
}
17.subOK
- subOK - Determine if can compute x-y without overflow
- Example: subOK(0x80000000,0x80000000) = 1, subOK(0x80000000,0x70000000) = 0
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 20
- Rating: 3
int subOK(int x, int y) {
int p=x^y;
int q=x^(x+~y+1);
return !((p&q)>>31);
}
**Sol ** 判断 x x x和 y y y是否同号和是否溢出,由溢出规则得负数-正数得正数,正数-负数得负数再次判断 x x x是否和溢出差值是否同号
18.bitParity
- bitParity - returns 1 if x contains an odd number of 0’s
- Examples: bitParity(5) = 0, bitParity(7) = 1
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 20
- Rating: 4
int bitParity(int x) {
x=x^(x>>16);
x=x^(x>>8);
x=x^(x>>4);
x=x^(x>>2);
x=x^(x>>1);
return x&1;
}
Sol PPT上例题,考虑并行即可
19.isPower2
- 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;
return ! ( (x^(x&y)) | (x>>31) | !x ) ;
}
**Sol ** 分别判断 x = = 2 n x==2^n x==2n , x x x是否为负数和x是否为0
优化
int isPower2(int x) {
int y=x>>31;
int p=x+(~y);
return !(p>>31 | (x&p));
}
Sol 将0和负数放在一起考虑,利用x&(x-1)一定会将最低有效位置成1
20.float_i2f
- 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.
- Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
- Max ops: 30
- Rating: 4
unsigned float_i2f(int x) {
if(!x) return x;
int sign=(x>>31)&1,H=31;
if(sign) x=-x;
unsigned ans,tmp=x;
int tag=1<<31;
while(!(tmp&tag)) {
tmp=tmp<<1;
H=H-1;
}
tmp=tmp<<1;
int exp=127+H;
int A=0x1ff,B=0x3ff;
int flag=0;
if((tmp&A) > 0x100) flag=1;
if((tmp&B) == 0x300) flag=1;
ans=((sign<<31) + (exp<<23) + (tmp>>9)) + flag;
return ans;
}
Sol 首先得到符号位和最高位,如果是负数就变成正数。然后主要根据四舍六入五成双的原则,如果最后9位是大于5(十进制数下)即大于0x100即进1,或最后10位前两位都是1即进1(即右移9位后小数点前一位和小数点后一位均为1)
优化 后面发现本题已经可以使用大常数了,就能直接再优化掉符号位的一些操作符,然后再利用if会占用一个操作符的特征,利用|来优化掉一个if
unsigned float_i2f(int x) {
if(!x) return x;
int sign=0,flag=0,H=158,tag=0x80000000;
if(x<0) {
x=-x;
sign=tag;
}
unsigned tmp=x;
while(!(tmp&tag)) {
tmp=tmp<<1;
H=H-1;
}
tmp=tmp<<1;
int exp=H;
int A=0x1ff,B=0x3ff;
flag=((tmp&A) > 0x100) | ((tmp&B) == 0x300);
unsigned ans=(sign + (exp<<23) + (tmp>>9)) + flag;
return ans;
}
21.leftBitCount
- leftBitCount - returns count of number of consective 1’s in left-hand (most significant) end of word.
- Examples: leftBitCount(-1) = 32, leftBitCount(0xFFF0F0F0) = 12
- Legal ops: ! ~ & ^ | + << >>
- Max ops: 50
- Rating: 4
int leftBitCount(int x) {
int p=!(~x);
int cnt=( !!(~(x>>16)) ) <<4;
cnt=cnt+((!!(~(x>>(cnt+8))))<<3);
cnt=cnt+((!!(~(x>>(cnt+4))))<<2);
cnt=cnt+((!!(~(x>>(cnt+2))))<<1);
cnt=cnt+(!!(~(x>>(cnt+1))));
return 32+~cnt+p;
}
Sol 利用并行思想,因为最高位一定是1,需要得到连续段的末尾1在哪里,即两种情况,前一半如果不全是1则进入前一半,前一百如果全是1则进入后一半。最后答案即是31-pos+1(pos为连续段1的末尾位置)
在测试特殊数据中发现0xffffffff和0xfffffffe会得到同样的答案故特判0xffffffff即可
后来一直在想优化,因为!!~的每次三个操作符太占位置了,于是考虑先对x取反,数连读段0的个数即可
int leftBitCount(int x) {
x=~x;
int Tmp=!x;
int num=0,tmp,upp;
tmp=!(x>>16);
upp=tmp<<4;
num=upp; x=x<<upp;
tmp=!(x>>24);
upp=tmp<<3;
num=num+upp; x=x<<upp;
tmp=!(x>>28);
upp=tmp<<2;
num=num+upp; x=x<<upp;
tmp=!(x>>30);
upp=tmp<<1;
num=num+upp; x=x<<upp;
num=num+!(x>>31)+Tmp;
return num;
}