[转]左移和右移

先说左移,左移就是把一个数的所有位都向左移动若干位,在C中用<<运算符.例如:

int i = 1;
i = i << 2; //把i里的值左移2位

也就是说,1的2进制是000...0001(这里1前面0的个数和int的位数有关,32位机器,gcc里有31个0),左移2位之后变成000...0100,也就是10进制的4,所以说左移1位相当于乘以2,那么左移n位就是乘以2的n次方了(有符号数不完全适用,因为左移有可能导致符号变化,下面解释原因)

需要注意的一个问题是int类型最左端的符号位和移位移出去的情况.我们知道,int是有符号的整形数,最左端的1位是符号位,即0正1负,那么移位的时候就会出现溢出,例如:

int i = 0x40000000; //16进制的40000000,为2进制的01000000...0000
i = i << 1;

那么,i在左移1位之后就会变成0x80000000,也就是2进制的100000...0000,符号位被置1,其他位全是0,变成了int类型所能表示的最小值,32位的int这个值是-2147483648,溢出.如果再接着把i左移1位会出现什么情况呢?在C语言中采用了丢弃最高位的处理方法,丢弃了1之后,i的值变成了0.

左移里一个比较特殊的情况是当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位,如:

int i = 1, j = 0x80000000; //设int为32位
i = i << 33;   // 33 % 32 = 1 左移1位,i变成2
j = j << 33;   // 33 % 32 = 1 左移1位,j变成0,最高位被丢弃

在用gcc编译这段程序的时候编译器会给出一个warning,说左移位数>=类型长度.那么实际上i,j移动的就是1位,也就是33%32后的余数.在gcc下是这个规则,别的编译器是不是都一样现在还不清楚.

总之左移就是: 丢弃最高位,0补最低位

再说右移,明白了左移的道理,那么右移就比较好理解了.

右移的概念和左移相反,就是往右边挪动若干位,运算符是>>.

右移对符号位的处理和左移不同,对于有符号整数来说,比如int类型,右移会保持符号位不变,例如:

int i = 0x80000000;
i = i >> 1; //i的值不会变成0x40000000,而会变成0xc0000000

就是说,符号位向右移动后,正数的话补0,负数补1,也就是汇编语言中的算术右移.同样当移动的位数超过类型的长度时,会取余数,然后移动余数个位.

总之,在C中,左移是逻辑/算术左移(两者完全相同),右移是算术右移,会保持符号位不变.实际应用中可以根据情况用左/右移做快速的乘/除运算,这样会比循环效率高很多.


例子:
-5>>3=-1
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1111 1111
其结果与 Math.floor((double)-5/(2*2*2)) 完全相同。

-5<<3=-40
1111 1111 1111 1111 1111 1111 1111 1011
1111 1111 1111 1111 1111 1111 1101 1000
其结果与 -5*2*2*2 完全相同。

5>>3=0
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0000 0000
其结果与 5/(2*2*2) 完全相同。

5<<3=40
0000 0000 0000 0000 0000 0000 0000 0101
0000 0000 0000 0000 0000 0000 0010 1000
其结果与 5*2*2*2 完全相同。

-5>>>3=536870911
1111 1111 1111 1111 1111 1111 1111 1011
0001 1111 1111 1111 1111 1111 1111 1111

无论正数、负数,它们的右移、左移、无符号右移 32 位都是其本身,比如 -5<<32=-5、-5>>32=-5、-5>>>32=-5。
一个有趣的现象是,把 1 左移 31 位再右移 31 位,其结果为 -1。
0000 0000 0000 0000 0000 0000 0000 0001
1000 0000 0000 0000 0000 0000 0000 0000
1111 1111 1111 1111 1111 1111 1111 1111(符号位向右移动后,正数的话补0,负数补1)

对于10进制的数字,左移一位就是在末尾加上一个0,数值变大10倍。
同理,对于二进制数字,左移一位是在末尾加上一个0,数值变大2被。
所以 x << 3,x就变大 2^3 倍,就是 8*x
右移同理

一般情况下你要乘或者是除以数字是2的次方的话都可以用的
执行速度快

 

 

C#是用<<(左移) >>(右移) 运算符是用来执行移位运算。

  左移 (<<)  将第一个操作数向左移动第二个操作数指定的位数,空出的位置补0

  左移相当于乘. 左移一位相当于乘2;左移两位相当于乘4;左移三位相当于乘8

  x<<1= x*2

  x<<2= x*4

  x<<3= x*8

  x<<4= x*16

  同理, 右移即相反:

  右移 (>>) 将第一个操作数向右移动第二个操作数所指定的位数,空出的位置补0

  右移相当于整除. 右移一位相当于除以2;右移两位相当于除以4;右移三位相当于除以8

  x>>1= x/2

  x>>2= x/4

  x>>3= x/8

  x>>4=x/16

  当声明重载C#移位运算符时,第一个操作数的类型必须总是包含运算符声明的类或结构,并且第二个操作数的类型必须总是 int,如:

class Program
    {
       
static void Main(string[] args)
        {
            ShiftClass shift1 =
new ShiftClass(5, 10);
            ShiftClass shift2 = shift1 <<
2;
            ShiftClass shift3 = shift1 >>
2;

            Console.WriteLine(
"{0} << 2 结果是:{1}", shift1.valA,  shift2.valA);
            Console.WriteLine(
"{0} << 2 结果是:{1}", shift1.valB,shift2.valB);
            Console.WriteLine(
"{0} >> 2 结果是:{1}", shift1.valA,  shift3.valA);
            Console.WriteLine(
"{0} >> 2 结果是:{1}", shift1.valB, shift3.valB);

            Console.ReadLine();
        }

       
public class ShiftClass
        {
          
public int valA;
          
public int valB;

           
public ShiftClass(int valA, int valB)
            {
               
this.valA = valA;
               
this.valB = valB;
            }

           
public static ShiftClass operator <<(ShiftClass shift, int count)
            {
               
int a = shift.valA << count;
               
int b = shift.valB << count;
               
return new ShiftClass(a, b);
            }

            
public static ShiftClass operator >>(ShiftClass shift, int count)
            {
               
int a = shift.valA >> count;
               
int b = shift.valB >> count;
               
return new ShiftClass(a, b);
            }

        }
    } 

以上表达式,输出结果是:

 5<<2结果是:20

 10<<2结果是:40

 5>>2结果是:1

 10>>2结果是:2

 

C#移位运算问题

public class exe

    {

        public static void Main()

        {

            int a, b, c;

            c = 0x1af034;

            a = (c >> 16) & 0xffff;

            b = c & 0x00ff;

            Console.WriteLine("a is {0}",a );

            Console.WriteLine("b is {0}",b );

            Console.Read();

        }

    }

请问这里为什么(c >> 16) & 0xffff的值会是26?还有c & 0x00ff的值为什么是52

 

1

c >> 16 = 0x1AF034 >> 16 = 0x00001A

0x00001A & 0x00FFFF = 0x00001A = 26

 

2

c & 0x0000FF = 0x1AF034 & 0x0000FF = 0x000034 = 52

 

位移运算就是将数字的二进制形式按指定的位数左移或右移,因为十六进制的每位数可以化成一个四位数的二进制的数字(2的四次方为16),所以在二进制中移动16位就是在十六进制中移动16/4 = 4位,那么0x1AF034 >> 16 = 0x00001A

 

按位且(&)这个运算符,这个运算符的意思是将数字转为二进制逐位进行且比较,按照111100000的规则,这样拆开来运算后再整合为结果

其实有个快捷方法,还是因为十六进制的每位数可以化成一个四位数的二进制的数字(2的四次方为16),而且0x0 = 0000B0xF = 1111B,也就是说如果对应位上数是F,那么这位上的数可以保留,否则变为0,如上面的0x0000FF & 0x1AF034,你可以看到前面的数字只有个位和十位上有数字,所以只保留后面数字的个位和十位就是结果,那么就是0x000034 = 52

 

 

posted on 2011-10-20 11:35  lynnjeans  阅读(2877)  评论(0编辑  收藏  举报