ICS Datalab

bitAnd - x&y using only ~ and |

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

bitConditional - x ? y : z for each bit respectively

/* 
 * bitConditional - x ? y : z for each bit respectively
 *   Example: bitConditional(0b00110011, 0b01010101, 0b00001111) = 0b00011101
 *   Legal ops: & | ^ ~
 *   Max ops: 8
 *   Rating: 1
 */
int bitConditional(int x, int y, int z) {
  return (x&y)|((~x)&z);
}

byteSwap - swaps the nth byte and the mth byte

注意从这里开始题目里允许了方便的 !+ 运算,这使得我们能够简单的进行条件判断了,也可以做 - 减法了。

另外,因为可能m == n,所以 a ^= b ^= a ^= b 会失败……不过反正那玩意也至少要4*3=12 ops,加上特判之后就很麻烦了。

/* 
 * 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 nn = n<<3;
	int mm = m<<3;
	int c = ((x>>nn)^(x>>mm))&0xFF;
	int d = x^(c<<nn)^(c<<mm);
    return d;
}

cleanConsecutive1 - change any consecutive 1 to zeros in the binary form of x.

经典题,但没见过的话会比较头疼。

/* 
 * cleanConsecutive1 - change any consecutive 1 to zeros in the binary form of x.
 *   Consecutive 1 means a set of 1 that contains more than one 1.
 *   Examples cleanConsecutive1(0x10) = 0x10
 *            cleanConsecutive1(0xF0) = 0x0
 *            cleanConsecutive1(0xFFFF0001) = 0x1
 *            cleanConsecutive1(0x4F4F4F4F) = 0x40404040
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 25
 *   Rating: 4
 */
int cleanConsecutive1(int x){
	int y = (x&(x<<1));
	y = y|(y>>1);
    return x^y;
}

countTrailingZero - return the number of consecutive 0 from the lowest bit of the binary form of x.

__builtin_ctz

/* 
 * countTrailingZero - return the number of consecutive 0 from the lowest bit of 
 *   the binary form of x.
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   Examples countTrailingZero(0x0) = 32, countTrailingZero(0x1) = 0,
 *            countTrailingZero(0xFFFF0000) = 16,
 *            countTrailingZero(0xFFFFFFF0) = 8,
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
int countTrailingZero(int x){
	int ans = (!(x&0x0000FFFF))<<4;
	ans += (!(x&(0x000000FF<<ans)))<<3;
	ans += (!(x&(0x0000000F<<ans)))<<2;
	ans += (!(x&(0x00000003<<ans)))<<1;
	ans += (!(x&(0x00000001<<ans)));
	ans += (!(x&(0x00000001<<ans)));
    return ans;
}

divpwr2 - Compute x/(2^n), for 0 <= n <= 30, round toward zero

我卡了很久,下面是我的自言自语。

/* 
 * 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) {
    // -n  = (~n) + 1
    // x/(2^n) 相当于x>>n
    // 一个方法是判断n是否<=4;
    // int le3 = !(n&(~3));
    // int eq4 = !(n^4);
    // int le4 = le3|eq4;
    // int m = (le4<<31)>>31; // le4 ? -1 : 0
    // int ans = x>>((1<<n)&m);
    // ----------------------
    // 最大的问题是负数该怎么办——因为负数是向下取整的,被要求转换成向上取整。如果是整除的情况,那就可以不用管它了。关键是没法整除的情况。
    // 一种想法是先把负数转成正数,然后再做除法,但是这因为定义域的不对称性会失败。
    // int pos_ans = x>>n;
    // int sgn = (x>>31);
    // int neg_x = (~(x)) + 1;
    // printf("%x %x %x\n", x, ~x, (~x)+1);
    // int neg_ans = (~(neg_x >> n)) + 1;
    // printf("%d %d %d %d %d %d\n", x, n, pos_ans, sgn, neg_x, neg_ans);
    // return ((~sgn)&pos_ans)|((sgn)&neg_ans);
    // -----------------------
    // 另外一种想法是检查整除性,题目里的 n <= 30 可能会在这方面有用。
    // 先搞出来 (1<<n)-1
    int mask = (1<<n)+(~0);
    int isdiv = !(x&mask); // 是否恰整除
    int isneg = (x>>31)&1;
    int ans = x>>n;
    // 如果没有恰整除且是负数则需要+1
    ans += isneg&(!isdiv);
    return ans;

这题有一个比较tricky的优化:

int divpwr2(int x, int n) {
	return (x + ((x>>31) & ((1<<n) + (~0))))>>n;
}

oneMoreThan - return 1 if y is one more than x, and 0 otherwise

不要想太复杂,既然允许加的话,就这么判断:(y == x+1) && y != 1<<31

/* 
 * oneMoreThan - return 1 if y is one more than x, and 0 otherwise
 *   Examples oneMoreThan(0, 1) = 1, oneMoreThan(-1, 1) = 0
 *   Legal ops: ~ & ! ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */

#define d(x) printf(#x),printf("=%d\n", x)
int oneMoreThan(int x, int y) {
    int b1 = !(y^(x+1));
    int b2 = !!(y^(1<<31));
    return b1&b2;
}

satMul3 - multiplies by 3, saturating to Tmin or Tmax if overflow

又卡了很久……实际上只要做两次加法之后分别判断是否溢出了就行。

/*
 * satMul3 - multiplies by 3, saturating to Tmin or Tmax if overflow
 *  Examples: satMul3(0x10000000) = 0x30000000
 *            satMul3(0x30000000) = 0x7FFFFFFF (Saturate to TMax)
 *            satMul3(0x70000000) = 0x7FFFFFFF (Saturate to TMax)
 *            satMul3(0xD0000000) = 0x80000000 (Saturate to TMin)
 *            satMul3(0xA0000000) = 0x80000000 (Saturate to TMin)
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 25
 *  Rating: 3
 */
int satMul3(int x) {
    int x31 = (x>>31);
    int sb = 1<<31;
    int sgn = x&sb;
    int xx = x+x;
    int of1 = (xx&sb)^sgn;
    int xxx = xx+x;
    int of2 = (xxx&sb)^sgn;
    int of = (of1|of2); // 0 if safe, (-1)<<31 if overflow
    int off = of>>31; // 0 if safe, -1 if overflow
    return ((~off)&xxx)|(off&((~x31)^(1<<31)));
}

这也有一个比较巧妙的优化:

int satMul3(int x) {
	int xx = x+x;
	int xxx = xx+x;
	int of = ((x^xx)|(x^xxx))>>31;
	return (of | xxx) ^ (of & ((x>>31) ^ (1<<31)));
}

subOK - Determine if can compute x-y without overflow

可以稍微讨论一下。(x >= 0) == (y >= 0) 时是 1,其他时候:

  1. (x < 0) && (y >= 0) 时,令x = (-1)<<31 + x1, y = 0 + y1,则 subOK(x, y) = (x-y >= (-1)<<31) = (x1 - y1 >= 0) = !(x1 - y1 < 0)
  2. (x >= 0) && (y < 0) 时,令x = 0 + x1, y = (-1)<<31 + y1,则 subOK(x, y) = (x-y < 1<<31) = (x1 - y1 < 0)

综上,subOK(x, y) = (y < 0) == (x1 - y1 < 0)

/* 
 * 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 sx = (x>>31)&1;
    int sy = (y>>31)&1;
    int mask = ~(1<<31);
    int x1 = x&mask;
    int y1 = y&mask;
    int sxy = ((x1 + (~y1) + 1)>>31)&1;
    int b1 = !(sx^sy);
    int b2 = !(sy^sxy);
    return b1|b2;
}

不过其实页不用讨论的这么复杂,利用一个比较好性质的话,只需要检测加法是否溢出。

int subOK(int x, int y) {
	int yx = y + (~x);
	return (((x ^ y) & (y ^ yx)) >> 31) + 1;
}

isLessOrEqual - if x <= y then return 1, else return 0

/* 
 * isLessOrEqual - if x <= y  then return 1, else return 0 
 *   Example: isLessOrEqual(4,5) = 1.
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 24
 *   Rating: 3
 */

(x <= y) = !(y < x) ,即判断 !((x >= 0 && y < 0) || (y的符号位 == x的符号位 && y去掉符号位 < x去掉符号位))

int isLessOrEqual(int x, int y) {
    int sx = (x>>31)&1;
    int sy = (y>>31)&1;
    int b1 = (!sx)&sy;
    int xx = x^(sx<<31);
    int yy = y^(sy<<31);
    int yymxx = yy+((~xx)+1);
    int sxy = (yymxx>>31)&1;
    int b2 = (!(sx^sy)) & sxy;
    return !(b1|b2);
}

这个优化真的非常玄妙,真不知道是怎么想到的:

int isLessOrEqual(int x, int y) {
	return ((x + ~(((x^y)>>31)|y))>>31)&1;

trueThreeFourths - multiplies by 3/4 rounding toward 0

/*
 * trueThreeFourths - multiplies by 3/4 rounding toward 0,
 *   avoiding errors due to overflow
 *   Examples: trueThreeFourths(11) = 8
 *             trueThreeFourths(-9) = -6
 *             trueThreeFourths(1073741824) = 805306368 (no overflow)
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 4
 */
int trueThreeFourths(int x)
{
	int x1 = x>>2;
	int x2 = x&3;
	int ans1 = (x1+x1+x1);
	// x2 = x%4 = 0, 1, 2, 3
	// 向零取整,意味着>=0时向下取整,<0时向上取整,即在/4前+3
	int sgn = (x>>31)&3;
	int ans2 = (x2+x2+x2+sgn)>>2;
	return ans1+ans2;
}

float_twice - Return bit-level equivalent of expression 2*f for floating point argument f.

/* 
 * 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) {
	unsigned s = uf&(1<<31);
	unsigned exp = (uf>>23)&0xff;
	unsigned ef = uf&0x7fffffff;
	// printf("uf=%x, s=%x, exp=%x")
	if (exp == 0xff) return uf;
	else {
		if (exp == 0xfe) {
			return s|0x7f800000;
		}
		if (exp == 0) {
			ef <<= 1;
		} else {
			ef += (1<<23);
		}
	}
	return s|ef;
}

float_i2f - Return bit-level equivalent of expression (float) x

/* 
 * 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
 */

直接暴力做,差点爆了ops限制,优化了一下:

unsigned float_i2f(int x) {
	// 不管怎样,首先把x拆成前23位和后23位……
	int i;
	int j;
	int s = x&0x80000000; // 1 ops
	int f;
	int e;
	// INT_MIN 太麻烦了,直接特判掉吧。
	if (x == 0x80000000) return 0xcf000000;
	// 0也是
	if (x == 0) return 0;
	// 负数也太麻烦了,直接转成正数处理吧。
	if (s) x = -x; // 1 ops
	// 获取最高位
	for (j = 0; x>>(j+1); ++j);
	e = 127 + j;
	x ^= (1<<j);
	if (j <= 23) {
		f = x<<(23-j);
	} else {
		f = x>>(j-23);
		i = 1 << (j-24);
		if ((x&i) && ( (f&1) || (x&(i-1)) )) {
			f += 1; // 1 ops
			if (f == 0x800000) {
				f = 0;
				e++;
			}
		}
	}
	return s|(e<<23)|f;
}

float_f2i - Return bit-level equivalent of expression (int) f for floating point argument f.

/* 
 * float_f2i - 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
 */
int float_f2i(unsigned uf) {
	int s;
	int e;
	int f;
	int x;
	s = uf&0x80000000;
	e = (uf^s)>>23;
	f = uf&0x7fffff;
	if (e >= 158) return 0x80000000u;
	if (e < 127) return 0;
	x = 0x800000^f;
	if (e >= 150) x <<= (e-150);
	else x >>= (150-e);
	if (s) x = -x;
	return x;
}

float_pwr2 - Return bit-level equivalent of the expression 2.0^x(2.0 raised to the power x) for any 32-bit integer x.

/* 
 * float_pwr2 - 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
 */
unsigned float_pwr2(int x) {
	if (x < -149) return 0;
	if (x < -126) return 1<<(x+149);
	if (x > 127) return 0x7f800000;
    return (x+127)<<23;
}

我完整的自言自语

/* 
 * CS:APP Data Lab 
 * 
 * 
 * 
 * bits.c - Source file with your solutions to the Lab.
 *          This is the file you will hand in to your instructor.
 *
 * WARNING: Do not include the <stdio.h> header; it confuses the dlc
 * compiler. You can still use printf for debugging without including
 * <stdio.h>, although you might get a compiler warning. In general,
 * it's not good practice to ignore compiler warnings, but in this
 * case it's OK.  
 */

#if 0
/*
 * Instructions to Students:
 *
 * STEP 1: Read the following instructions carefully.
 */

You will provide your solution to the Data Lab by
editing the collection of functions in this source file.

INTEGER CODING RULES:
 
  Replace the "return" statement in each function with one
  or more lines of C code that implements the function. Your code 
  must conform to the following style:
 
  int Funct(arg1, arg2, ...) {
      /* brief description of how your implementation works */
      int var1 = Expr1;
      ...
      int varM = ExprM;

      varJ = ExprJ;
      ...
      varN = ExprN;
      return ExprR;
  }

  Each "Expr" is an expression using ONLY the following:
  1. Integer constants 0 through 255 (0xFF), inclusive. You are
      not allowed to use big constants such as 0xffffffff.
  2. Function arguments and local variables (no global variables).
  3. Unary integer operations ! ~
  4. Binary integer operations & ^ | + << >>
    
  Some of the problems restrict the set of allowed operators even further.
  Each "Expr" may consist of multiple operators. You are not restricted to
  one operator per line.

  You are expressly forbidden to:
  1. Use any control constructs such as if, do, while, for, switch, etc.
  2. Define or use any macros.
  3. Define any additional functions in this file.
  4. Call any functions.
  5. Use any other operations, such as &&, ||, -, or ?:
  6. Use any form of casting.
  7. Use any data type other than int.  This implies that you
     cannot use arrays, structs, or unions.

 
  You may assume that your machine:
  1. Uses 2s complement, 32-bit representations of integers.
  2. Performs right shifts arithmetically.
  3. Has unpredictable behavior when shifting an integer by more
     than the word size.

EXAMPLES OF ACCEPTABLE CODING STYLE:
  /*
   * pow2plus1 - returns 2^x + 1, where 0 <= x <= 31
   */
  int pow2plus1(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     return (1 << x) + 1;
  }

  /*
   * pow2plus4 - returns 2^x + 4, where 0 <= x <= 31
   */
  int pow2plus4(int x) {
     /* exploit ability of shifts to compute powers of 2 */
     int result = (1 << x);
     result += 4;
     return result;
  }

FLOATING POINT CODING RULES

For the problems that require you to implent floating-point operations,
the coding rules are less strict.  You are allowed to use looping and
conditional control.  You are allowed to use both ints and unsigneds.
You can use arbitrary integer and unsigned constants.

You are expressly forbidden to:
  1. Define or use any macros.
  2. Define any additional functions in this file.
  3. Call any functions.
  4. Use any form of casting.
  5. Use any data type other than int or unsigned.  This means that you
     cannot use arrays, structs, or unions.
  6. Use any floating point data types, operations, or constants.


NOTES:
  1. Use the dlc (data lab checker) compiler (described in the handout) to 
     check the legality of your solutions.
  2. Each function has a maximum number of operators (! ~ & ^ | + << >>)
     that you are allowed to use for your implementation of the function. 
     The max operator count is checked by dlc. Note that '=' is not 
     counted; you may use as many of these as you want without penalty.
  3. Use the btest test harness to check your functions for correctness.
  4. Use the BDD checker to formally verify your functions
  5. The maximum number of ops for each function is given in the
     header comment for each function. If there are any inconsistencies 
     between the maximum ops in the writeup and in this file, consider
     this file the authoritative source.

/*
 * STEP 2: Modify the following functions according the coding rules.
 * 
 *   IMPORTANT. TO AVOID GRADING SURPRISES:
 *   1. Use the dlc compiler to check that your solutions conform
 *      to the coding rules.
 *   2. Use the BDD checker to formally verify that your solutions produce 
 *      the correct answers.
 */


#endif
/* Copyright (C) 1991-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */
/* This header is separate from features.h so that the compiler can
   include it implicitly at the start of every compilation.  It must
   not itself include <features.h> or any other header that includes
   <features.h> because the implicit include comes before any feature
   test macros that may be defined in a source file before it first
   explicitly includes a system header.  GCC knows the name of this
   header in order to preinclude it.  */
/* glibc's intent is to support the IEC 559 math functionality, real
   and complex.  If the GCC (4.9 and later) predefined macros
   specifying compiler intent are available, use them to determine
   whether the overall intent is to support these features; otherwise,
   presume an older compiler has intent to support these features and
   define these macros by default.  */
/* wchar_t uses Unicode 10.0.0.  Version 10.0 of the Unicode Standard is
   synchronized with ISO/IEC 10646:2017, fifth edition, plus
   the following additions from Amendment 1 to the fifth edition:
   - 56 emoji characters
   - 285 hentaigana
   - 3 additional Zanabazar Square characters */
/* 
 * bitAnd - x&y using only ~ and | 
 *   Example: bitAnd(6, 5) = 4
 *   Legal ops: ~ |
 *   Max ops: 8
 *   Rating: 1
 */
int bitAnd(int x, int y) {
  return ~((~x)|(~y));
}
/* 
 * bitConditional - x ? y : z for each bit respectively
 *   Example: bitConditional(0b00110011, 0b01010101, 0b00001111) = 0b00011101
 *   Legal ops: & | ^ ~
 *   Max ops: 8
 *   Rating: 1
 */
int bitConditional(int x, int y, int z) {
  return (x&y)|((~x)&z);
}
/* 
 * 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) { 
	// TODO
	// 注意到题目里给了方便的 ! 与 + 运算,这使得我们能够简单的进行条件判断了,也可以做 - 了。
	// printf("%d %d %d %d->%d %d->%d\n", x, m, n, (x>>(n<<3)), (x>>(n<<3))&0xFF, (x>>(m<<3)), (x>>(m<<3))&0xFF);
	// a ^= b ^= a ^= b 失败了……不过反正那玩意也要4*3=12 ops,加上特判之后就很麻烦了。
	int nn = n<<3;
	int mm = m<<3;
	int a = (x>>nn)&0xFF;
	int b = (x>>mm)&0xFF;
	int d = (x^(a<<nn)^(b<<mm))|(a<<mm)|(b<<nn);
//	x ^= ((x>>(n<<3))&0xFF)<<(m<<3);
//	x ^= ((x>>(m<<3))&0xFF)<<(n<<3);
//	x ^= ((x>>(n<<3))&0xFF)<<(m<<3);
    return d;
}
/* 
 * 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) {
	// 我们最终要获得一个 n ? 0 : (1<<31)>>(n-1)
	// (1<<31)>>(n-1) = ((1<<31)>>n)<<1
	int mask = ((1<<31)>>n)<<1;
	return (x>>n)&(~mask);
}
/* 
 * cleanConsecutive1 - change any consecutive 1 to zeros in the binary form of x.
 *   Consecutive 1 means a set of 1 that contains more than one 1.
 *   Examples cleanConsecutive1(0x10) = 0x10
 *            cleanConsecutive1(0xF0) = 0x0
 *            cleanConsecutive1(0xFFFF0001) = 0x1
 *            cleanConsecutive1(0x4F4F4F4F) = 0x40404040
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 25
 *   Rating: 4
 */
int cleanConsecutive1(int x){
	int y = (x&(x<<1));
	y = y|(y>>1);
    return x^y;
}
/* 
 * countTrailingZero - return the number of consecutive 0 from the lowest bit of 
 *   the binary form of x.
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   YOU MAY USE BIG CONST IN THIS PROBLEM, LIKE 0xFFFF0000
 *   Examples countTrailingZero(0x0) = 32, countTrailingZero(0x1) = 0,
 *            countTrailingZero(0xFFFF0000) = 16,
 *            countTrailingZero(0xFFFFFFF0) = 8,
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 40
 *   Rating: 4
 */
int countTrailingZero(int x){
	int ans = (!(x&0x0000FFFF))<<4;
	ans += (!(x&(0x000000FF<<ans)))<<3;
	ans += (!(x&(0x0000000F<<ans)))<<2;
	ans += (!(x&(0x00000003<<ans)))<<1;
	ans += (!(x&(0x00000001<<ans)));
	ans += (!(x&(0x00000001<<ans)));
    return ans;
}
/* 
 * 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) {
	// -n  = (~n) + 1
	// x/(2^n) 相当于x>>n
	// 一个方法是判断n是否<=4;
	// int le3 = !(n&(~3));
	// int eq4 = !(n^4);
	// int le4 = le3|eq4;
	// int m = (le4<<31)>>31; // le4 ? -1 : 0
	// int ans = x>>((1<<n)&m);
	// ----------------------
	// 最大的问题是负数该怎么办——因为负数是向下取整的,被要求转换成向上取整。如果是整除的情况,那就可以不用管它了。关键是没法整除的情况。
	// 一种想法是先把负数转成正数,然后再做除法,但是这因为定义域的不对称性会失败。
	// int pos_ans = x>>n;
	// int sgn = (x>>31);
	// int neg_x = (~(x)) + 1;
	// printf("%x %x %x\n", x, ~x, (~x)+1);
	// int neg_ans = (~(neg_x >> n)) + 1;
	// printf("%d %d %d %d %d %d\n", x, n, pos_ans, sgn, neg_x, neg_ans);
    // return ((~sgn)&pos_ans)|((sgn)&neg_ans);
	// -----------------------
	// 另外一种想法是检查整除性,题目里的 n <= 30 可能会在这方面有用。
	// 先搞出来 (1<<n)-1
	int mask = (1<<n)+(~0);
	int isdiv = !(x&mask); // 是否恰整除
	int isneg = (x>>31)&1;
	int ans = x>>n;
	// 如果没有恰整除且是负数则需要+1
	ans += isneg&(!isdiv);
	return ans;
}
/* 
 * oneMoreThan - return 1 if y is one more than x, and 0 otherwise
 *   Examples oneMoreThan(0, 1) = 1, oneMoreThan(-1, 1) = 0
 *   Legal ops: ~ & ! ^ | + << >>
 *   Max ops: 15
 *   Rating: 2
 */

#define d(x) printf(#x),printf("=%d\n", x)
int oneMoreThan(int x, int y) {
	// 我想复杂了,既然允许加的话,就这么判断:(y == x+1) && y != 1<<31
	int b1 = !(y^(x+1));
	int b2 = !!(y^(1<<31));
	return b1&b2;
	/*
	// 即判断是否 y-x == 1——不对,又在边界情况炸掉了。
	int ymx = y&
	// 需要再额外判断是否有 y>x ,即判断 (y >= 0 && x < 0) || (y的符号位 == x的符号位 && y去掉符号位 > x去掉符号位)
	int sy = (y>>31)&1;
	int sx = (x>>31)&1;
	int b1 = (!sy)&sx;
	int yy = y^(sy<<31);
	int xx = x^(sx<<31);
	int xxmyy = xx+((~yy)+1);
	int syx = (xxmyy>>31)&1;
	int b2 = (!(sx^sy)) & syx;
	d(x), d(y);
	d(sx), d(sy);
	d(b1), d(b2);
	return b1|b2;
	*/
	// --------------------
	// int ymx = y + ((~x)+1);
	// printf("%d %d %d\n", x, y, ymx);
	// return !(ymx^1)&((!y)|(!!(~(x|y))));
}
/*
 * satMul3 - multiplies by 3, saturating to Tmin or Tmax if overflow
 *  Examples: satMul3(0x10000000) = 0x30000000
 *            satMul3(0x30000000) = 0x7FFFFFFF (Saturate to TMax)
 *            satMul3(0x70000000) = 0x7FFFFFFF (Saturate to TMax)
 *            satMul3(0xD0000000) = 0x80000000 (Saturate to TMin)
 *            satMul3(0xA0000000) = 0x80000000 (Saturate to TMin)
 *  Legal ops: ! ~ & ^ | + << >>
 *  Max ops: 25
 *  Rating: 3
 */
int satMul3(int x) { // TODO
	// 0x7FFFFFFF / 3 = 0x25555555 + 0x05555555 = 0x2AAAAAAA
	/* ops
	// 如何判断是否溢出?与 0x2AAAAAAA 相比较?
	// ans = (x >= 0 ? (x > 0x2AAAAAAA ? 0x7FFFFFFF : 3*x) : (x < 0xD5555555 ? 0x80000000 : 3*x)
	// 果然还是太复杂了。
	int sgn = (x>>31)&1; // 2 ops
	int smax = (((((0x2A<<8)|0xAA)<<8)|0xAA)<<8)|0xAA; // 0x2AAAAAAA, 6 ops
	int smin = (~smax)^(3<<30); // 0xD5555555, 3 ops
	int gt = ((smax + (~x) + 1)>>31)&1; // 5 ops
	int lt = ((x + (~smin) + 1)>>31)&1; // 5 ops
	int pans = 
	*/
	/*
	// 把x, y理解成大整数试试,分成x = x1<<8 + x2, y = y1<<8 + y2
	// 那么,当x >= 0时, (x*3 <= 0x7FFFFFFF) == (((x1*3)<<8) + x2*3 <= 0x7FFFFFFF)
	//                                       == (x1*3 + ((x2*3)>>8) <= 0x7FFFFF)
	// 令y=x1*3+((x2*3)>>8)                  == !(y > 0x800000) == !(y > (1<<23))
	//                                       == !((1<<23) - y < 0) ? -- TODO
	// 当 x < 0 时,     (x*3 >= 0x80000000) == (x1*3 + ((x2*3)>>8) >= (-1)<<23)
	//                                       == !(y + (1<<23) < 0)
	// 又太复杂了……
	int x1 = x>>8;
	int x2 = x&0xFF;
	int y = (x1+x1+x1) + ((x2+x2+x2)>>8);
	int sgn = ((x>>31)&1)<<31;
	int exp1 = ((1<<23) + (~y) + 1);
	int b1 = ~(((exp1>>31)&1)<<31);
	int exp2 = (y + (1<<23));
	int b2 = ~(((exp2>>31)&1)<<31);
    return ((~sgn)&();
	*/
	/* 尝试把前2位和后30位分别判断
	int x1 = x&(3<<30);
	int sgn2 = (x&(1<<31))>>1;
	int b1 = !(sgn2^x1); // 0 -> overflow, 1 -> ok
	int x2 = x^x1; // == x mod 2^30
	int b2 = ((x2+x2+x2)>>31)&1; // when x > 0, 1 -> overflow, 0 -> ok
	*/
	// --------------------
	/* 试着每一次运算过后检测是否溢出。毕竟同符号数的+运算的溢出是很容易检测的。
	int x31 = x>>31;
	int sgn = x31&1;
	int y = x^x31; // -1 if x < 0 else 0
	int yy = y+y;
	int of1 = (yy>>31)&1;
	int yyy = yy+y;
	int xxx = x+x+x;
	int of2 = (yyy>>31)&1;
	int of = of1|of2; // 0, 1
	int off = of + (~0); // -1, 0
	// int offf = (~of) + 1; // 0, -1
	return (off&xxx)|((~off)&((~x31)^(1<<31)));
	*/
	int x31 = (x>>31);
	int sb = 1<<31;
	int sgn = x&sb;
	int xx = x+x;
	int of1 = (xx&sb)^sgn;
	int xxx = xx+x;
	int of2 = (xxx&sb)^sgn;
	int of = (of1|of2); // 0 if safe, (-1)<<31 if overflow
	int off = of>>31; // 0 if safe, -1 if overflow
	// d(x), d(of1), d(of2), d(of), d(off);
	return ((~off)&xxx)|(off&((~x31)^(1<<31)));
}
/* 
 * 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) {
	// (x >= 0) == (y >= 0) 时是 1,其他时候:
	// (x < 0) && (y >= 0) 时,x = (-1)<<31 + x1, y = 0 + y1,
	//   subOK(x, y) = (x-y >= (-1)<<31) = (x1 - y1 >= 0) = !(x1 - y1 < 0)
	// (x >= 0) && (y < 0) 时,x = 0 + x1, y = (-1)<<31 + y1,
	//   subOK(x, y) = (x-y < 1<<31) = (x1 - y1 < 0)
	// 综上,subOK(x, y) = (y < 0) == (x1 - y1 < 0)
	int sx = (x>>31)&1;
	int sy = (y>>31)&1;
	int mask = ~(1<<31);
	int x1 = x&mask;
	int y1 = y&mask;
	int sxy = ((x1 + (~y1) + 1)>>31)&1;
	int b1 = !(sx^sy);
	int b2 = !(sy^sxy);
	return b1|b2;
}
/* 
 * 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) {
	// (x <= y) = !(y < x) ,即判断 !((x >= 0 && y < 0) || (y的符号位 == x的符号位 && y去掉符号位 < x去掉符号位))
	int sx = (x>>31)&1;
	int sy = (y>>31)&1;
	int b1 = (!sx)&sy;
	int xx = x^(sx<<31);
	int yy = y^(sy<<31);
	int yymxx = yy+((~xx)+1);
	int sxy = (yymxx>>31)&1;
	int b2 = (!(sx^sy)) & sxy;
// 	d(x), d(y);
// 	d(sx), d(sy);
// 	d(yymxx);
// 	d(b1), d(b2);
	return !(b1|b2);
}
/*
 * trueThreeFourths - multiplies by 3/4 rounding toward 0,
 *   avoiding errors due to overflow
 *   Examples: trueThreeFourths(11) = 8
 *             trueThreeFourths(-9) = -6
 *             trueThreeFourths(1073741824) = 805306368 (no overflow)
 *   Legal ops: ! ~ & ^ | + << >>
 *   Max ops: 20
 *   Rating: 4
 */
int trueThreeFourths(int x)
{
	int x1 = x>>2;
	int x2 = x&3;
	int ans1 = (x1+x1+x1);
	// x2 = x%4 = 0, 1, 2, 3
	// 向零取整,意味着>=0时向下取整,<0时向上取整,即在/4前+3
	int sgn = (x>>31)&3;
	int ans2 = (x2+x2+x2+sgn)>>2;
	return ans1+ans2;
}
/* 
 * 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) {
	unsigned s = uf&(1<<31);
	unsigned exp = (uf>>23)&0xff;
	unsigned ef = uf&0x7fffffff;
	// printf("uf=%x, s=%x, exp=%x")
	if (exp == 0xff) return uf;
	else {
		if (exp == 0xfe) {
			return s|0x7f800000;
		}
		if (exp == 0) {
			ef <<= 1;
		} else {
			ef += (1<<23);
		}
	}
	return s|ef;
}
/* 
 * 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) {
	// 运算数过多了……还需要削减一点 TODO
	// 不管怎样,首先把x拆成前23位和后23位……
	int i;
	int j;
	int s = x&0x80000000; // 1 ops
	int f;
	int e;
	// INT_MIN 太麻烦了,直接特判掉吧。
	if (x == 0x80000000) return 0xcf000000;
	// 0也是
	if (x == 0) return 0;
	// 负数也太麻烦了,直接转成正数处理吧。
	if (s) x = -x; // 1 ops
	// 获取最高位
	for (j = 0; x>>(j+1); ++j);
	e = 127 + j; // 1 ops
	x ^= (1<<j); // 2 ops
	if (j <= 23 /*x < 0x1000000 (1<<24)*/) {
		f = x<<(23-j); // 2 ops
	} else {
		f = x>>(j-23); // 2 ops
		i = 1 << (j-24); // 2 ops
		if ((x&i) && ( (f&1) || (x&(i-1)) )) { // 6 ops
			f += 1; // 1 ops
			if (f == 0x800000 /*(1<<23)*/) {
				f = 0;
				e++; // 1 ops
			}
		}
	}
	// printf("%d %d %d", s, e, f);
	return s|(e<<23)|f; // 3 ops
	/* 很久以前写的代码,懒得去理解了……
	int s = x&(1<<31);
	int hb;
	int i;
	int exp;
	int frac;
	if (x == 0) return 0;
	if (x < 0) {
	   if (x == s) return 0xcf000000;
	   x = -x;
	}
	hb = -1;
	for (i = 0; i < 31; i++) {
		if ((x>>i)&1) hb = i;
	}
	exp = 127+hb;
	frac = x^(1<<hb);
	if (hb > 23) {
		frac >>= (hb-23);
	}
	// printf("%d %d %d\n", hb, exp);
	// printf("%x %d %x %x\n", s, exp, exp<<23, frac);
	return s|(exp<<23)|frac;
	*/
}
/* 
 * float_f2i - 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
 */
int float_f2i(unsigned uf) {
	/* 
	    Comparing bits.c:float_f2i to tests.c:test_float_f2i .. Mismatch
		float_f2i(1241513984) --> 2048 [0x800]
		test_float_f2i(1241513984) --> 2097152 [0x200000]
		.. A genuine counterexample
	*/
	int s;
	int e;
	int f;
	int x;
	// uf = 1241513984;
	s = uf&0x80000000;
	e = (uf^s)>>23;
	f = uf&0x7fffff;
	// printf("%u %d %d %d %d\n", uf, s, e, f, x);
	if (e >= 158) return 0x80000000u;
	if (e < 127) return 0;
	x = 0x800000^f;
	// printf("%u %d %d %d %d\n", uf, s, e, f, x);
	if (e >= 150) x <<= (e-150);
	else x >>= (150-e);
	if (s) x = -x;
	// printf("%d\n", x);
	return x;
}
/* 
 * float_pwr2 - 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
 */
unsigned float_pwr2(int x) {
	// printf("%d\n", x);
	if (x < -149) return 0;
	if (x < -126) return 1<<(x+149);
	if (x > 127) return 0x7f800000;
    return (x+127)<<23;
}
posted @ 2022-09-23 14:45  frank3215  阅读(1699)  评论(0编辑  收藏  举报