位运算的地位

 以下内容摘自《枕边算法书》感觉对于位运算描述的很形象

  对于程序员来说,”位” 相当于现实世界中构成水和空气的粒子。
编程世界的所有东西都会从位开始,以位结束。刚步入编程世界的
初学者看到整数就是整数,看到字符串就是字符串。但功力深厚的
程序员眼中无论整数还是字符串,它们都是位。

  与系统编程不同,一般应用程序的编程对位运算的要求并不高。
即使如此,很少有程序员不懂位运算,因为在不懂位运算的情况下
编程写程序,就像在现实世界中不呼吸、不喝水一样,几乎不可能。

位运算的类型

运算符 含义
& 按位与:全1才1,否则都0
| 按位或:有1得1,全0才0
^ 按位异或:相异为真,相同为假
>> 右移:对于有符号数的右移分为算术右移和逻辑右移
<< 左移:比较简单不论是否有符号整数
~ 取反:每个位1变0,0变1
对于正整数而言,左移1位就是x2,右移就是除2
下图来自《Computer Systems - A Programmer’s Perspective》
bit-right-shift.PNG
算术右移:直接右移n位,左侧空出来的n位全补0
逻辑右移:对于有符号数有些不同,对于无符号数没有区别
可惜对于右移而言标准c没有规定,应该使用哪种,大多数的编译器都使用算术右移
java中明确规定x>>k运算符表示x算术右移k位, x>>>k表示逻辑右移

原码

一个数的二进制表示加上正负号

反码(Ones’ complement /一的补码)

定义
bit-fanma.png
式中,N为真值,n为编码的位数

反码,正数的反码等于其原码,而负数的反码则可以通过保留其符号位,将原码的数值位取反得到。

补码(Twos’ complement /二的补码)

  bit-buma.PNG
  计算机中采用补码的原因是原码和反码的时候,0的表示不唯一

补码的计算方法

  正数的补码和原码一致,辅助的补码用用其绝对值的原码按位取反,然后(从LSB(最低有效位))+1

根据补码(2补数)求真值的方法

  设1100为补码, 拿到补码最高位是-(2^3), 然后 + 2^2 得到 -4

总结

  1. 现代计算机中存储的永远是补码
  2. 运算的时候也是用补码在运算
  3. x,y均为整数, 如果有~x = y 成立,则有 x + y = -1 成立

例题

  1. ~3 = ?
    3 = (0000 0011)按位取反得到(1111 1100) 按照2补数解释为-4
  2. 1
    2
    3
    int cat = 13;
    cat - ~-cat; // this experssion's value is 1
    putchar (~-~-~-cat); // this line equal to putchar('\n'); \n 的ascii code 十进制的值为 10

位运算可以用来判断整数的奇偶性

1
2
3
4
5
6
7
// 输出0 ~ 100 的所有奇数
for(int i = 0; i < 100; i++) {
if (i & 1) { // 基数最低bit位是1
std::cout<<i<<" ";
}
}
std::cout<< std::endl;