datalab

csapp

Posted by farmer3-c on September 12, 2025

CS:APP Data Lab

The purpose of this lab is to become more familiar with bit-level representations of integers andfloating point numbers. You’ll do this by solving a series of programming “puzzles.” Many of these puzzles are quite artificial, but you’ll find yourself thinking much more about bits in working your way through them.———— Harry Bovik

bitXor

  • bitXor - x^y using only ~ and &
  • Example: bitXor(4, 5) = 1
  • Legal ops: ~ &
  • Max ops: 14
  • Rating: 1
1
2
3
4
5
int bitXor(int x, int y)
{
  // x^y=(x&~y)|(~x&y)=~(~x&~y)&~(x&y)
  return ~(~(x & ~y) & ~(~x & y));
}

tmin

  • tmin - return minimum two’s complement integer
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 4
  • Rating: 1
1
2
3
4
int tmin(void)
{
  return 1 << 31;
}

isTmax

  • isTmax - returns 1 if x is the maximum, two’s complement number, and 0 otherwise
  • Legal ops: ! ~ & ^ +
  • Max ops: 10
  • Rating: 1
1
2
3
4
int isTmax(int x)
{
  return !(~(x + 1) ^ x) & !!(x + 1);
}

allOddBits

  • allOddBits - return 1 if all odd-numbered bits in word set to 1
  • where bits are numbered from 0 (least significant) to 31 (most significant)
  • Examples: allOddBits(0xFFFFFFFD) = 0, allOddBits(0xAAAAAAAA) = 1
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 12
  • Rating: 2
1
2
3
4
5
6
int allOddBits(int x)
{
  int m = (0xAA << 8) | 0xAA;
  m = (m << 16) | m;
  return !((x & m) ^ m);
}

negate

  • negate - return -x
  • Example: negate(1) = -1.
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 5
  • Rating: 2
1
2
3
4
int negate(int x)
{
  return ~x + 1;
}

isAsciiDigit

  • isAsciiDigit - return 1 if 0x30 <= x <= 0x39 (ASCII codes for characters ‘0’ to ‘9’)
  • Example: isAsciiDigit(0x35) = 1.
  • isAsciiDigit(0x3a) = 0.
  • isAsciiDigit(0x05) = 0.
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 15
  • Rating: 3
1
2
3
4
5
6
int isAsciiDigit(int x)
{
  int a = x + (~0x30 + 1);
  int b = 0x39 + (~x + 1);
  return !((a >> 31) | (b >> 31));
}

conditional

  • conditional - same as x ? y : z
  • Example: conditional(2,4,5) = 4
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 16
  • Rating: 3
1
2
3
4
int conditional(int x, int y, int z)
{
  return ((~(!!x) + 1) & y) | ((~(~(!!x) + 1)) & z);
}

isLessOrEqual

  • isLessOrEqual - if x <= y then return 1, else return 0
  • Example: isLessOrEqual(4,5) = 1.
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 24
  • Rating: 3
1
2
3
4
5
6
7
8
int isLessOrEqual(int x, int y)
{
  int a = (x >> 31) & 1, b = (y >> 31) & 1;
  int f = a ^ b;
  int d = y + ~x + 1;
  int ds = (d >> 31) & 1;
  return (f & a) | (!f & !ds);
}

logicalNeg

  • logicalNeg - implement the ! operator, using all of the legal operators except !
  • Examples: logicalNeg(3) = 0, logicalNeg(0) = 1, logicalNeg(-5) = 0
  • Legal ops: ~ & ^ + « »
  • Max ops: 12
  • Rating: 4
1
2
3
4
int logicalNeg(int x)
{
  return (((~x + 1) | x) >> 31) + 1;
}

howManyBits

  • howManyBits - return the minimum number of bits required to represent x in two’s complement
  • Examples: howManyBits(12) = 5
  • howManyBits(298) = 10
  • howManyBits(-5) = 4
  • howManyBits(0) = 1
  • howManyBits(-1) = 1
  • howManyBits(0x80000000) = 32
  • Legal ops: ! ~ & ^ + « »
  • Max ops: 90
  • Rating: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int howManyBits(int x)
{
  int sign = x >> 31, bit16, bit8, bit4, bit2, bit1;
  x = (sign & ~x) | (~sign & x);
  bit16 = (!!(x >> 16)) << 4;
  x >>= bit16;
  bit8 = (!!(x >> 8)) << 3;
  x >>= bit8;
  bit4 = (!!(x >> 4)) << 2;
  x >>= bit4;
  bit2 = (!!(x >> 2)) << 1;
  x >>= bit2;
  bit1 = (!!(x >> 1));
  x >>= bit1;
  return bit16 + bit2 + bit4 + bit8 + bit1 + x + 1;
}

floatScale2

  • floatScale2 - 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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
unsigned floatScale2(unsigned uf)
{
  unsigned sign = (uf >> 31) & 1, exp = (uf >> 23) & 0xff, frac = uf & 0x7fffff;
  if (exp == 0xff)
    return uf;
  if (exp == 0)
  {
    if (frac & 0x800000)
    {
      exp = 1;
    }
    frac <<= 1;
    return (exp << 23) | (sign << 31) | frac;
  }
  exp++;
  if (exp == 0xff)
  {
    return (sign << 31) | (0xff << 23);
  }
  return (sign << 31) | (exp << 23) | frac;
}

floatFloat2Int

  • floatFloat2Int - Return bit-level equivalent of expression (int) f for floating point argument f.
  • Argument is passed as unsigned int, but it is to be interpreted as the bit-level representation of a single-precision floating point value.
  • Anything out of range (including NaN and infinity) should return 0x80000000u.
  • Legal ops: Any integer/unsigned operations incl.   , &&. also if, while
  • Max ops: 30
  • Rating: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int floatFloat2Int(unsigned uf)
{
  int sign = uf >> 31, exp = ((uf >> 23) & 0xff) - 127, frac = (uf & 0x7fffff) | 0x800000;
  if (exp >= 31)
    return 0x80000000u;
  if (exp < 0)
    return 0;
  if (exp < 23)
  {
    frac >>= (23 - exp);
  }
  else
  {
    frac <<= (exp - 23);
  }
  if (sign)
  {
    frac = -frac;
  }
  return frac;
}

floatPower2

  • floatPower2 - Return bit-level equivalent of the expression 2.0^x (2.0 raised to the power x) for any 32-bit integer x.
  • The unsigned value that is returned should have the identical bit representation as the single-precision floating-point number 2.0^x.
  • If the result is too small to be represented as a denorm, return 0. If too large, return +INF.
  • Legal ops: Any integer/unsigned operations incl.   , &&. Also if, while
  • Max ops: 30
  • Rating: 4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
unsigned floatPower2(int x)
{
  if (x < -148)
  {
    return 0;
  }
  if (x < -126)
  {
    unsigned f = x + 148;
    unsigned fra = 1 << f;
    return fra;
  }
  if (x <= 127)
  {
    int k = x + 127;
    return k << 23;
  }
  return 0x7f800000;
}