CS:APP Lab 1 - Data Lab

这里就不写具体题目要求了,直接上代码

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

// 提取第 n 字节
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 的个数
int bitCount(int x) {
  // 分治法, 相当于把里面全部的 1 求和
  // 那么分两段,总 = (左 >> (位宽 / 2)) + 右
  // 一直分到只有两位,最后就相当于把所有的 1 都加起来
  // 这里有个蛋疼的问题,带上 -O 优化参数,会使得编译器会把超出 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 的数字 x 和 -x 其中一个最高位也就是符号位是一定是 1
  // 而 0 和 -0 最高位都是 0

  // 确保得到最高位的数字 x | (-x)
  int a = x | (~x + 1);

  // 把最高位的数字移到最低位
  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 位的补码表示的数字
  // 与 -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
  // 也就是说 *不能被整除* 的 *负数* 得到结果后加 1 就可以

  // 把最高位移下来可以得出是否为负数
  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 是否为正数
int isPositive(int x) {
  // 首先最高位是 1 就是负数
  // !操作就是说不是负数,但是 0 也不是正数,所以要排除
  // 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);
  
  // 两个数字符号不同时会有溢出可能,但是符号不同时只有 x 为负数时返回 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 位
  // 这个结果不仅代表数值还代表下一次搜索的位置
  // 以此类推
  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 在哪里,最高的一代表了指数
  // 尾数是 23 位
  // 注意到整数不会出现非规格数,所以尾数中不需要最高位 1,因为浮点的定义中尾数有隐含的 1
  // 分为两种情况:
  // 指数小于等于 23,左移至满 23 位
  // 指数大于 23,此时有舍入的问题,取最后所有位和移位后的最后一位,axxx
  // xxx 是会被干掉的部分,注意此处不是限定三位,是后面所有位
  // 向偶数舍入:
  // a = 1 且 xxx = 100... 则入 1
  // a = 0 且 xxx = 100... 则舍去
  // xxx > 100... 时入 1
  // xxx < 100... 时舍去

  if (!x) return x;

  // 提取符号并转为正数
  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;
  }
  // 得到指数
  e = e + 1;


  int low = 0;

  // 尾数 mask
  int mask = ~((1 << 31) >> 8);

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

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

    // 舍入前的尾数
    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) {// 失去位在正中间,且前一位是奇数,入 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;

  // 提取尾数
  int f = uf & 0x007FFFFF;

  // 原始赋值
  int ans = uf;

  // 非规格化情况
  if (e == 0) {
    // 符号 + 尾数左移一位
    // 主要归功于定义使得规格和非规格化之间可以平滑过渡
    // 所以此处不需要担心尾数移位直接进入阶码的情况
    // 非规格化数的尾数左移移位至多使得阶码加一,刚刚好代表了同样的意思
    ans = s + (f << 1);
  } else if (e != t) {
    // 符号 + (阶码 + 1) + 尾数
    ans = s + e + (1 << 23) + f;
  }
  // 如果是 NaN 或者 无穷大,直接无视掉

  return ans;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容