位操作

来源:互联网 发布:孟加拉语翻译软件 编辑:程序博客网 时间:2024/05/17 23:11

转载自:http://www.ahathinking.com/archives/76.html


========目录=======

--位运算简介

--打印整型二进制 | 获取原码、反码、补码

--位运算技巧

==================

位运算简介

位运算的基本运算符有:and(&), or(|), xor(^), not(~), shl(<<), shr(>>),一般来讲,位运算符通常用于unsigned类型的整数。关于这六种运算符的简介,matrix67总结的很好,这里手抄一遍,部分做了修改和补充,加入了我的理解。

=== 1. and 运算 ===

and 运算通常用于二进制取位操作,例如一个数 and 1 的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制最末位为0表示偶数,最末位为1表示位奇数。

and 运算符通常和一个称为“屏蔽码(mask)”的操作数一起使用,例如上面的1。屏蔽字是指定位置为1的整数值,用来选择一个值的某些位时隐藏其他位。and与屏蔽码一起使用有很多用处,例如

  • 将指定位置0
  • 测试某个位是1还是0

=== 2. or 运算 ===

or运算通常用于二进制特定位上的无条件赋值(将指定位置1,例如一个数 or 1 的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1 之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。

=== 3. xor 运算 ===

xor 运算通常用于对二进制的特定位进行取反操作,因为异或可以这样定义:01异或0都不变,异或1则取反

xor 运算的逆运算是它本身。也就是说两次异或同一个数最后结果不变,即(a xor b)xor b = a。xor 运算可以用于简单的加密,比如我想对MM说1314520,但怕别人知道,于是双方约定拿我的生日19880516作为密钥。1314520 xor 19880516 = 2066500,我就把20665500告诉MM。MM再次计算20665500 xor 19880516的值,得到1314520,于是她就明白了我的企图。 (matrix好有爱,哈哈)

逆运算可以延伸另外一个神奇的东西,例如,加法和减法互为逆运算,并且满足交换律。利用它们我们可以写出一个不需要临时变量的swap过程。

void swap(int &a, int &b)  // 注,可能会导致越界(a+b){    a = a + b;    b = a - b;    a = a - b;}

刚才说xor的逆运算是它本身,于是我们就有了一个看起来非常诡异的swap过程:

void swap(int &a, int &b){    a = a ^ b;    b = a ^ b;    a = a ^ b;}

注,这样做木有用中间变量来的快,毕竟需要计算嘛,使用中间变量只是简单的赋值。

=== 4. not 运算 ===

not运算的定义是把内存中的0和1全部取反。根据我最近看书及做题的经验,not运算符经常配合移位运算符来制造“屏蔽码”,例如下面就是很常用的屏蔽码

  • ~0 << n  右端n位置0
  • ~(~0 << n) 右端n位置1

=== 5. shl 运算 ===

a shl b 表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 shl 2 = 400。可以看出,a shl b的值实际上就是a乘以2b次方

通常认为a shl1 比a*2更快,因为前者更底层一些。因此程序中乘以2的操作请尽量用左移一位来代替

此外,定义一些屏蔽码一般会用到shl运算,例如 1 << n 就是比较常用的屏蔽码。

再如很多算法和数据结构要求数据规模必须是2的幂,此时可以用shl来定义Max_N等常量,在下一篇位排序我们会看到。

=== 6. shr 运算 ===

和shl相似,a shr b 表示把a转为二进制后右移b位(去掉末b位),相当于a除以2的b次方(取整)。因此我们也经常shr 1来代替div 2,比如二分查找,堆的插入操作等等。

想办法用shr代替除法运算可以使程序效率大大提高,例如最大公约数的二进制算法用除以2操作代替慢的出奇的mod运算,效率可以提高60%。(木有验证)

这里附上最大公约数的二进制算法

// 思想,先提取2的幂,然后辗转相减int gcd(int x, int y)  //注,只适合处理整型{    int i, j;    if(0 == x)  return y;    if(0 == y)  return x;    for(i = 0; 0 == (x & 1); ++i) //提取2的幂    {        x >>= 1;    }    for(j = 0; 0 == (y & 1); ++j)    {        y >>= 1;    }    if(i > j)    i = j;    //辗转相减    while(1)    {        if(x < y) // 保证每次相减x>y        {            int t = x; x = y; y = t; // 使用中间变量,就不用异或了吧        }        x -= y;        if(0 == x)        {            return y<<i;        }        while(0 == (x & 1)) // 去掉2的幂        {            x >>= 1;        }    }}


========================================================

打印整型二进制 | 获取原码、反码、补码

这一部分,在了解了位运算的基本知识后,我们实现了打印整型二进制,获取原码、反码和补码的代码,如下

#include<stdio.h> #define MAX 32 void IntToBinary(char binary[], int x);  // x转换为二进制void IntToBinary2(char binary[], int x); // 不同的思路可以有多种方法void TrueForm(char binary[], int x);    // 获得x的原码void RadixMinus(char binary[], int x);  // 获得x的反码void Complement(char binary[], int x);  // 获得x的补码 void main(){    char binary[MAX+1] = {0};    int number;    printf("输入要测试的整型:");    scanf("%d",&number);    IntToBinary2(binary,number);    printf("%d 的二进制表示: %s\n",number,binary);    TrueForm(binary,number);    printf("%d 的原码: %s\n",number,binary); } void IntToBinary(char binary[], int x) // 由高位到低位转换{    for(unsigned int i = 0x80000000, j = 0; i > 0; i >>= 1)    {        binary[j++] = x & i ? '1':'0';    }} void IntToBinary2(char binary[], int x) // 由低位到高位转换{    for(int j = MAX-1; j >= 0; --j)    {        binary[j] = (x & 1) + '0';        x >>= 1;    }} void TrueForm(char binary[], int x) // 对于负数,计算机存储的是补码{    if(x >= 0)    {        IntToBinary(binary,x);    }else    {        IntToBinary(binary,(1<<(MAX-1))-x); // 由原码数学公式得 | 1<<n <=> 2^n    }} void RadixMinus(char binary[], int x){    if(x >= 0)    {        IntToBinary(binary,x);    }else    {        IntToBinary(binary,x-1);  // 计算机存储的是补码,负数的反码等于补码减一    }} void Complement(char binary[], int x){    IntToBinary(binary,x);}

========================================================

位运算技巧

通过之前的整理,可以总结一下一些常用的位运算基本技巧,如下,设真值x,位数n

  • 判断奇偶:x & 1
  • 除以2的n次幂(乘法左移):x >> n
  • 对2的n次幂取余:x & ~(~0<<n)
  • 从低位开始,将x的第n位置1:x | (1<<n)
  • 从低位开始,将x的第n位置0:x & ~(1<<n)
  • 测试x的第n位是否为1:x & (1<<n)
  • 最大的int (01 11111111 11111111 11111111): MAX_INT = ~(1<<31)
  • 最小的int (10 00000000 000000000 00000000):MIN_INT = 1<<31

(全文完)

参考文献:

http://www.matrix67.com/blog/archives/263

http://kenby.iteye.com/blog/1011480


======================分割线===

我自己的总结:

要保存不变: 跟1与、  跟0或;

要置1: 跟1或;

要置0: 跟0与;

原创粉丝点击