I have learned a lot about how the computer make decisions and discretions with bit-level operations. Also, although brain-burning, using Bitwise operations in computation intensive-programming can substancially boost performance and efficiency. So I think these stuff could be useful for my programming contests.
Here I have collected the labs problems and other puzzles I solved.
Bit Cowboy Lab – Integars
int bitAnd(int x, int y)
{
/* get ~(x&y) using ~x|~y */
int c= ~x | ~y;
return ~c;
}
/*
* getByte - Extract byte n from word x
* Bytes numbered from 0 (LSB) to 3 (MSB)
* Examples: getByte(0x12345678,1) = 0x56
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 6
* Rating: 2
*/
int getByte(int x, int n)
{
/* shift the word right and leave the disired two digits at the right end, the get using mask */
int mask=0xff;
n=n<<3;
x=x>>(n);
return x&mask;
}
/*
* 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)
{
/* shift the word arithmatically, then use mask to clear the left-end bytes */
int mask = (1<<31)>>n; //shift only 31 to prevent overflow
mask = mask<<1;
mask=~mask;
x=x>>n;
return x&mask;
}
/*
* bitCount - returns count of number of 1's in word
* Examples: bitCount(5) = 2, bitCount(7) = 3
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 40
* Rating: 4
*/
int bitCount(int x)
{
/* use mask to check bits , since have operator limits, I count every 4 bits,
then shift them rightward step by step, then add them together in the end*/
int mask1, mask2, mask3, sum;
mask1 = 0x11 | (0x11 << 8); //cover 16 bits, 1 for every 4 bits
mask2 = mask1 | (mask1 << 16);//cover 32 bits, just twice as long as mask1
mask3 = 0xF|(0xF<<8); // mask first 8 bits
sum =x & mask2; // count 1's
sum = sum + ((x >> 1) & mask2);
sum = sum + ((x >> 2) & mask2);
sum = sum + ((x >> 3) & mask2);
//sum calculates the 1's is first 4 bits and other unwanted value
sum = sum+ (sum>>16); // get rid of unwanted value
sum = (sum & mask3) + ((sum >> 4) & mask3);
// limit maximum to 6 bits, because largest decimal value is 32.
return((sum + (sum >> 8)) & 0x3F);
}
/*
* bang - Compute !x without using !
* Examples: bang(3) = 0, bang(0) = 1
* Legal ops: ~ & ^ | + << >>
* Max ops: 12
* Rating: 4
*/
int bang(int x)
{
/* x or negative x will have 1 as thier first digit, but 0 will not*/
int mask=0x1;
int negative = ~x + 1;
int a = ((x >> 31) & mask);
int b= ((negative >> 31) & mask);
// one of a or b will be 1 if x is not 0
return (a|b)^mask;
}
/*
* tmin - return minimum two's complement integer
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 4
* Rating: 1
*/
int tmin(void)
{
/*juest need to get 0x100000.....*/
return 1<<31;
}
/*
* 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)
{
/*if the bits from the left end till n are all same, all 0 or all 1, then the leftside
digits can be abadoned and express the same value with only n bits, so the key is to
determine wether the first 32 - n bits are all same*/
int high= 32 + (~n+1);
int shift= (x<<high)>>high; // this changes all hige order bits to 1 or 0;
return !(shift^x); // check whether x is still same
}
/*
* 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)
{
/*needs to round toward 0, so need to know whether x is positive*/
int mask=(1<<n) + ~0;
int bias= (x>>31)&mask;
return (x+bias)>>n;
}
/*
* negate - return -x
* Example: negate(1) = -1.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 5
* Rating: 2
*/
int negate(int x)
{
/* ~x equals -(2max-abs(x)), thus needs to plus 1*/
return ~x+1;
}
/*
* isPositive - return 1 if x > 0, return 0 otherwise
* Example: isPositive(-1) = 0.
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 8
* Rating: 3
*/
int isPositive(int x)
{
/* if is negative, the first digit is 1, other wise is 0*/
//then just get the first digit;
int mask=0x1<<31;
int firstdigit=x&(mask); // first digit is 0 if x is negative , 1 if x is positive or 0
return !(firstdigit>>31|!x); //using |!x to count for 0
}
/*
* 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)
{
/* if x and y has same sign, check the sign of thier differnece, if x and y has different signs
return 0 if x is positive and if x is negative;*/
int sign, isLess, dif, equal, isLessorEqual;
sign = x ^ y;
isLess = ~sign;
dif = y + (~x + 1);
equal = !dif;
sign = sign & x;
dif = ~dif;
isLess = isLess & dif;
isLess = isLess | sign;
isLess = isLess & (1 << 31);
isLessorEqual = (!!isLess) | (equal);
return isLessorEqual;
}
/*
* ilog2 - return floor(log base 2 of x), where x > 0
* Example: ilog2(16) = 4
* Legal ops: ! ~ & ^ | + << >>
* Max ops: 90
* Rating: 4
*/
int ilog2(int x)
{
/* the value of log2 in binary is actually represented by how many digits are used, the
answer shoudld be the number of digits used minus one. I use a method similar to binary search
to find the most significant 1*/
int out=0;
int a=(!!(x>>16))<<31>>31;// if most sig is in right, a is false, if in left 16 digits a is true;
out += a & 16;
x = x>>(a & 16); //shift 16 if is on left side,
a = (!!(x>>8)) <<31>>31;
out += a & 8;
x = x>> (a & 8);
a = (!!(x>>4))<<31>>31;
out += a & 4;
x = x>> (a & 4);
a = (!!(x>>2))<<31>>31;
out += a & 2;
x = x>> (a & 2);
a = (!!(x>>1))<<31>>31;
out += a & 1;
return out;
}
Bit Cowboy Lab – Floating numbers
/*
* float_neg - Return bit-level equivalent of expression -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 representations of
* single-precision floating point values.
* When argument is NaN, return argument.
* Legal ops: Any integer/unsigned operations incl. ||, &&. also if, while
* Max ops: 10
* Rating: 2
*/
unsigned float_neg(unsigned uf)
{
/*need to negate the first digit since it represents sign, and keep every thing else;
use mask to detect special case NaN, and return original
*/
unsigned exp= uf>>23&0xFF;
unsigned fra= uf<<9;
unsigned a= 0x1<<31;
if ((exp==0xFF)&&(!!fra))
{
return uf;
}
return uf^a;
}
/*
* 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)
{
return 2;
}
/*
* float_twice - 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 float_twice(unsigned uf)
{
/* The challenege here is to handle differnet situations , we need to return
the number for Nan and Infi, add exp by one for normal form and add 1 to the number for
denormalized form. I seperate the three parts of the float, make the necessary changes, and
combine them to return*/
unsigned sign = uf>>31 << 31;
unsigned exp = (((uf << 1) >> 24) << 23);
unsigned fra= (uf << 9) >> 9;
if (exp==0x7F800000)
{
return uf; //inifinity or NaN
}
else if(exp==0)
{
return ((uf << 1) | sign); // if in denormal form, shift left by 1 and add sign
}
else
{
exp = (exp + (1 << 23)); //exp + 1 for normal
}
return(sign|exp|fra);