CSAPP 第二章的一道关于位运算的题目,答案看不懂,各位帮忙想想。

2019-03-04 17:40:18 +08:00
 lxy42

题目是 2.66 ,大致意思如下:

实现下面的函数:

int leftmost_one(unsigned x);

该函数返回 x 最左边的(高位) 1 的掩码,例如:x = 0xFF00 返回 0x8000x = 0x6600 返回 0x4000

题目要求只能使用位运算。

答案如下:

int leftmost_one(unsigned x){
    x |= (x >> 16);
    x |= (x >> 8);
    x |= (x >> 4);
    x |= (x >> 2);
    x |= (x >> 1);
    return x ^ (x >> 1);
}

我只推测出前面 5 行右移操作之后,x 变为从最左边的 1 到最右边全为 1 的形式(例如:0x6600 -> 0x7FFF )。

3479 次点击
所在节点    编程
5 条回复
bumz
2019-03-04 19:48:19 +08:00
然后举个例子你就明白了,比如 00001111 ^ 00000111 = 00001000
qysz
2019-03-04 20:29:49 +08:00
我对照函数得理解:
1. 想要求得掩码,需要得到掩码位左边全为零,右边全为一得状态,再最后异或一次就得到结果;
2. 考虑掩码左边,每次或操作后都能保证仍旧为零;
3.考虑掩码右边,最极端情况下就是只有掩码为 1,剩下得全为零,在这种情况下,折半位移并进行或运算了 5 次,最
多填充了 2 的 5 次方(32)个位置的 1 (而且是不重复的) ,也就是 5 次运算保证了掩码右边全为 1 ;
punderson
2019-03-04 20:52:50 +08:00
我对答案的理解:
从最后一行看起,它使得 x 最左边的 1 的右边全是 1 了。这样的话 0x01100110 成为 0x01111111。前面 5 次异或的运算,可以这样看,把最左边的一个 1,经过右移动 n 位得到右边的 1,而 n 可以由 1,2,4,8,16 组成。所以右边任意的 n 位都可以变成 1 了。
lxy42
2019-03-05 11:04:26 +08:00
认真手写推导了一遍,有点理解了前面 5 次右移的原理了。
bumz
2019-03-05 13:04:59 +08:00
原来题主问的前 5 步。。。

一个证明的框架:

要证明
前 5 步将 00001xxx 形式变为 00001111 形式
只需证明
前 5 步将 00001000 形式变为 00001111 形式

这种情况只需模拟一遍就得到答案了
所以也就证明了前 5 步将 00001xxx 形式变为 00001111 形式

证毕(细节自行补充)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/540948

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX