CS:APP Lab 1 - Data Lab

這里就不寫具體題目要求了,直接上代碼

// 不用 & 求 &
int bitAnd(int x, int y) {
  return ~(~x | ~y);
}

// 提取第 n 字節(jié)
int getByte(int x, int n) {
  // 就是移動 8 位的問題,8位就是 << 3
  int a = n << 3;
  return (x >> a) & 0xFF;
}

// 邏輯左移
int logicalShift(int x, int n) {
  // x + ~x + 1 = 0
  // -x = ~x + 1
  // 31 - n = 31 + (~x + 1)
  int k = 32 + (~n);

  // 單純的左移會溢出,<< n 其中 n 的值被定義為 0 到 31
  // https://stackoverflow.com/questions/7401888/why-doesnt-left-bit-shift-for-32-bit-integers-work-as-expected-when-used
  
  // 可以得到包括第 k 和高于第 k 的位為零其他位為 1
  int a = (~0) + (1 << k);

  // 再把第 k 位給加回來
  a = a + (1 << k);

  // 通過 & 把前面的 1 都干掉
  return (x >> n) & a;
}

// 求 1 的個數(shù)
int bitCount(int x) {
  // 分治法, 相當于把里面全部的 1 求和
  // 那么分兩段,總 = (左 >> (位寬 / 2)) + 右
  // 一直分到只有兩位,最后就相當于把所有的 1 都加起來
  // 這里有個蛋疼的問題,帶上 -O 優(yōu)化參數(shù),會使得編譯器會把超出 INT_MAX 的部分干掉
  // https://stackoverflow.com/questions/47934277/why-does-clang-produces-wrong-results-for-my-c-code-compiled-with-o1-but-not-wi

  // 0x5 = 0101
  // 0x3 = 0011
  // 0x0F = 0000 1111
  // 0x00FF = 0000 0000 1111 1111
  // 0x0000FFFF = 0000 0000 0000 0000 1111 1111 1111 1111
  int mask1 = 0x55 | (0x55 << 8); // = 0x5555
  mask1 = mask1 + (mask1 << 16); //  = 0x5555 5555
  int mask2 = 0x33 | (0x33 << 8); // = 0x3333
  mask2 = mask2 | (mask2 << 16); //  = 0x3333 3333
  int mask3 = 0x0F | (0x0F << 8); // = 0x0F0F
  mask3 = mask3 | (mask3 << 16); //  = 0x0F0F 0F0F
  int mask4 = 0xFF | (0xFF << 16); //= 0x00FF 00FF
  int mask5 = 0xFF | (0xFF << 8); // = 0x0000 FFFF

  int ans = (x & mask1) + ((x >> 1) & mask1);
  ans = (ans & mask2) + ((ans >> 2) & mask2);
  ans = (ans & mask3) + ((ans >> 4) & mask3);
  ans = (ans & mask4) + ((ans >> 8) & mask4);
  ans = (ans & mask5) + ((ans >> 16) & mask5);

  return ans;
}

// 不用 !, 求 !
int bang(int x) {
  // 非 0 的數(shù)字 x 和 -x 其中一個最高位也就是符號位是一定是 1
  // 而 0 和 -0 最高位都是 0

  // 確保得到最高位的數(shù)字 x | (-x)
  int a = x | (~x + 1);

  // 把最高位的數(shù)字移到最低位
  int b = a >> 31;

  // 取反,相當于對每一位求 !
  int c = ~b;

  // 把除最低位以外其他位都干掉
  return c & 1;
}

// 最小 int
int tmin(void) {
  return 1 << 31;
}

// 判斷 x 能否放進 n 位的空間里
int fitsBits(int x, int n) {
  // 先左移到最高位,再右移回來,會自動獲得算術右移的結果
  // 這樣得到的結果是相當于 n 位的補碼表示的數(shù)字
  // 與 -x 相加最后得到 0 即為可以表示,非零則無法表示
  // 時刻記住 -x = ~x + 1

  int k = 32 + (~n + 1);
  return !(((x << k) >> k) + ~x + 1);
}

// x/(2^n)
int divpwr2(int x, int n) {
  // 首先 x >> n 的自然結果是向下 round
  // 也就是說 *不能被整除* 的 *負數(shù)* 得到結果后加 1 就可以

  // 把最高位移下來可以得出是否為負數(shù)
  int isXNegative = (x >> 31) & 1;
  
  // 求得 x 的低 n 位是否為 0,是則能被整除,否則不能被整除
  int xNotDivisible = x & ~((1 << 31) >> (31 + ~n + 1));

  // 通過兩次 ! 可以得到 0 或 1
  return (x >> n) + (isXNegative & !!xNotDivisible);
}

// -x
int negate(int x) {
  return ~x + 1;
}

// x 是否為正數(shù)
int isPositive(int x) {
  // 首先最高位是 1 就是負數(shù)
  // !操作就是說不是負數(shù),但是 0 也不是正數(shù),所以要排除
  // x <> 0 時 !x = 0 且 !!x = 1
  // 依舊是通過兩次 ! 得到一個 0 或者 1
  return !((x >> 31) & 1) & !!x;
}

// x <= y
int isLessOrEqual(int x, int y) {
  int isXNegative = (x >> 31) & 1;
  int isYNegative = (y >> 31) & 1;
  int isNegative = ((x + ~y + 1) >> 31) & 1;
  int isZero = !(x + ~y + 1);
  
  // 兩個數(shù)字符號不同時會有溢出可能,但是符號不同時只有 x 為負數(shù)時返回 1
  return ((isNegative | isZero) & 
          ((isXNegative & isYNegative) | 
           (!isXNegative & !isYNegative))) | 
          (isXNegative & !isYNegative);
}

// return floor(log base 2 of x)
int ilog2(int x) {
  // 問題可以轉化為 x 最高的 1 在哪個位置上,結果即為 0 到 31
  // 這題,二分搜索?

  int ans = 0;
  // 搜索的條件如下
  // 移動 16 如果說不等于 0 那么意味著高 16 位有 1,則答案至少為 16,也就是 1 << 4
  // 如果說等于 0 那么就在低 16 位
  // 這個結果不僅代表數(shù)值還代表下一次搜索的位置
  // 以此類推
  ans = ans + ((!!(x >> (16 + ans))) << 4); // = 0 or = 16
  ans = ans + ((!!(x >> (8 + ans))) << 3);
  ans = ans + ((!!(x >> (4 + ans))) << 2);
  ans = ans + ((!!(x >> (2 + ans))) << 1);
  ans = ans + ((!!(x >> (1 + ans))) << 0);

  return ans;
}

// 浮點符號反轉
unsigned float_neg(unsigned uf) {
  unsigned k = uf & (0x7FFFFFFF);
  if (k > 0x7f800000) {
    return uf;
  }
  return uf + 0x80000000; // uf ^ 0x80000000 也可以
}

// int to float
unsigned float_i2f(int x) {
  // 有些費勁,寫了很久,但其實理清思路并不是難,而是是否清晰的理解 IEEE 的浮點定義
  // 首先 0 直接返回
  // 接下來開始尋找最高的 1 在哪里,最高的一代表了指數(shù)
  // 尾數(shù)是 23 位
  // 注意到整數(shù)不會出現(xiàn)非規(guī)格數(shù),所以尾數(shù)中不需要最高位 1,因為浮點的定義中尾數(shù)有隱含的 1
  // 分為兩種情況:
  // 指數(shù)小于等于 23,左移至滿 23 位
  // 指數(shù)大于 23,此時有舍入的問題,取最后所有位和移位后的最后一位,axxx
  // xxx 是會被干掉的部分,注意此處不是限定三位,是后面所有位
  // 向偶數(shù)舍入:
  // a = 1 且 xxx = 100... 則入 1
  // a = 0 且 xxx = 100... 則舍去
  // xxx > 100... 時入 1
  // xxx < 100... 時舍去

  if (!x) return x;

  // 提取符號并轉為正數(shù)
  int isXNegative = (x >> 31) & 1;
  int k = x;
  if (isXNegative) {
    k = ~x + 1; // -x
  }
  
  // 尋找最高位的 1
  int e = 31;
  int t = 0;
  while (t == 0 && e >= 0) {
    t = k & (1 << e);
    e = e - 1;
  }
  // 得到指數(shù)
  e = e + 1;


  int low = 0;

  // 尾數(shù) mask
  int mask = ~((1 << 31) >> 8);

  int needRound = 0;
  if (e > 23) {
    // 失去的位數(shù)
    int lost = e - 23;

    // 失去位的 mask
    int fmask = mask >> (23 - lost);

    // 舍入前的尾數(shù)
    low = (k >> lost) & mask;
    
    // 失去位的值
    int f = k & fmask;
    // 用于對比的正中間值,即 100...
    int p = 1 << (lost - 1);
    // 臨近失去位的一位值的mask
    int lmask = 1 << lost;
    // 臨近失去位的一位值
    int l = k & lmask;

    
    if (f > p) {// 失去位大于正中間,入 1
      needRound = 1;
    } else if (f == p && l == lmask) {// 失去位在正中間,且前一位是奇數(shù),入 1
      needRound = 1;
    }

  } else { // 右移或者不移動沒有舍入問題
    low = (k << (23 - e)) & mask;
  }

  // 實際的階碼
  e = (e + 127) << 23;

  return (isXNegative << 31) + e + low + needRound;
}

// 乘 2
unsigned float_twice(unsigned uf) {
  // 相對于上一道要簡單許多,按照 IEEE 的定義來計算即可
  
  int t = 0x7F800000;

  // 提取符號
  int s = uf & 0x80000000;

  // 提取階碼
  int e = uf & t;

  // 提取尾數(shù)
  int f = uf & 0x007FFFFF;

  // 原始賦值
  int ans = uf;

  // 非規(guī)格化情況
  if (e == 0) {
    // 符號 + 尾數(shù)左移一位
    // 主要歸功于定義使得規(guī)格和非規(guī)格化之間可以平滑過渡
    // 所以此處不需要擔心尾數(shù)移位直接進入階碼的情況
    // 非規(guī)格化數(shù)的尾數(shù)左移移位至多使得階碼加一,剛剛好代表了同樣的意思
    ans = s + (f << 1);
  } else if (e != t) {
    // 符號 + (階碼 + 1) + 尾數(shù)
    ans = s + e + (1 << 23) + f;
  }
  // 如果是 NaN 或者 無窮大,直接無視掉

  return ans;
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容