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) {
  // 先左移到最高位,再右移回來邪狞,會自動獲得算術(shù)右移的結(jié)果
  // 這樣得到的結(jié)果是相當于 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 的自然結(jié)果是向下 round
  // 也就是說 *不能被整除* 的 *負數(shù)* 得到結(jié)果后加 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) {
  // 問題可以轉(zhuǎn)化為 x 最高的 1 在哪個位置上,結(jié)果即為 0 到 31
  // 這題蠕蚜,二分搜索尚洽?

  int ans = 0;
  // 搜索的條件如下
  // 移動 16 如果說不等于 0 那么意味著高 16 位有 1,則答案至少為 16靶累,也就是 1 << 4
  // 如果說等于 0 那么就在低 16 位
  // 這個結(jié)果不僅代表數(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;
}

// 浮點符號反轉(zhuǎn)
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;

  // 提取符號并轉(zhuǎn)為正數(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ī)格化之間可以平滑過渡
    // 所以此處不需要擔(dān)心尾數(shù)移位直接進入階碼的情況
    // 非規(guī)格化數(shù)的尾數(shù)左移移位至多使得階碼加一,剛剛好代表了同樣的意思
    ans = s + (f << 1);
  } else if (e != t) {
    // 符號 + (階碼 + 1) + 尾數(shù)
    ans = s + e + (1 << 23) + f;
  }
  // 如果是 NaN 或者 無窮大门驾,直接無視掉

  return ans;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末耳贬,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子猎唁,更是在濱河造成了極大的恐慌咒劲,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件诫隅,死亡現(xiàn)場離奇詭異腐魂,居然都是意外死亡,警方通過查閱死者的電腦和手機逐纬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門蛔屹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人豁生,你說我怎么就攤上這事兔毒。” “怎么了甸箱?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵育叁,是天一觀的道長。 經(jīng)常有香客問我芍殖,道長豪嗽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任豌骏,我火速辦了婚禮龟梦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘窃躲。我一直安慰自己计贰,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布蒂窒。 她就那樣靜靜地躺著躁倒,像睡著了一般赎婚。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上樱溉,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天挣输,我揣著相機與錄音,去河邊找鬼福贞。 笑死撩嚼,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的挖帘。 我是一名探鬼主播完丽,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼拇舀!你這毒婦竟也來了逻族?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤骄崩,失蹤者是張志新(化名)和其女友劉穎聘鳞,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體要拂,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡抠璃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脱惰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搏嗡。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖拉一,靈堂內(nèi)的尸體忽然破棺而出采盒,到底是詐尸還是另有隱情,我是刑警寧澤蔚润,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布磅氨,位于F島的核電站,受9級特大地震影響抽碌,放射性物質(zhì)發(fā)生泄漏悍赢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一货徙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧皮胡,春花似錦痴颊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽锌杀。三九已至,卻和暖如春泻仙,著一層夾襖步出監(jiān)牢的瞬間糕再,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工玉转, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留突想,地道東北人。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓究抓,卻偏偏與公主長得像猾担,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子刺下,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內(nèi)容