黑马程序员技术交流社区

标题: 运算符>>>求解???? [打印本页]

作者: HM汪磊    时间: 2013-2-28 12:55
标题: 运算符>>>求解????
class  Yiwei
{
        public static void main(String[] args)
                {
                        int x=6;
                        x=x>>>32;
                        System.out.println("x="+x);       
                }

}


此程序输出结果为X=6.求解???运算符>>>有什么特殊要求吗???

作者: ╯两路人_、陌路    时间: 2013-2-28 13:42
class  Yiwei
{
        public static void main(String[] args)
                {
                        int x=6;     //x  赋值
                        x=x>>>32;  //X=6 右移32位  也就是 000000000000000000000000000000006
                        System.out.println("x="+x);        
                }

}


>>>是不带符号位的右移,x>>>32就是x的内容右移一位,开头补0(x的内容并不改变)   个人理解 希望正确
作者: 赵海洋    时间: 2013-2-28 14:27
这个吧,首先要明白这个原码和算术移位以及逻辑移位。
x>>>32,是指x右移32位。这个是指不带符号的算术移位,也就是不管左移还是右移,都补零。而逻辑移位呢,是带符号的移位,此处不详细解释。
x=6,计算机里存储是补码形式存储,而正数的补码和原码是一样的,所以算术移位后还是6.
希望你能理解。。。。。。
作者: 刘凯    时间: 2013-2-28 14:32
╯两路人_、陌路 发表于 2013-2-28 13:42
class  Yiwei
{
        public static void main(String[] args)

计算机存储是按二进制补码形式存储的 6 在计算机中的存储是 00000000 00000000 00000000 00000110  由于是>>> 无符号的右移  所以低位舍去 高位补0

对于byte、short、char和int进行移位时,规定实际移 动的次数是移动次数和32的余数,也就是移位33次和移位1次得到的结果相同。移动long型的数值时,规定实际移动的次数是移动次数和64的余数,也就 是移动66次和移动2次得到的结果相同。  

楼主的x为int类型   
硬件把移位对字长进行取模,右移32位就是右移了0位(CPU 把右移的位数 32 对于字长 32 进行取模运算,得到 0),因此6 右移 32 位仍然是 6。




作者: 刘凯    时间: 2013-2-28 14:35
赵海洋 发表于 2013-2-28 14:27
这个吧,首先要明白这个原码和算术移位以及逻辑移位。
x>>>32,是指x右移32位。这个是指不带符号的算术移位 ...

二楼 有点不是正确吧  这么说来x=6 >>>1  后也等于6喽?   可以验证下  等3 的
作者: 赵海洋    时间: 2013-2-28 14:47
刘凯 发表于 2013-2-28 14:35
二楼 有点不是正确吧  这么说来x=6 >>>1  后也等于6喽?   可以验证下  等3 的 ...

右移一位少个0,最后两位11,是3啊。我不是在说右移32位怎么移的么。。。看来还是没说明白,表达能力有点差。。。:dizzy:
作者: 赵海洋    时间: 2013-2-28 14:50
好吧,我应该这么说,对于这个6,就这么理解,换成32位的二进制,往右移位,左边补零,右边去掉,然后如果字长是32位的,小于32位就移,大于32就取模。怎么感觉我还是没说明白似的。。。。
作者: 刘凯    时间: 2013-2-28 14:51
赵海洋 发表于 2013-2-28 14:47
右移一位少个0,最后两位11,是3啊。我不是在说右移32位怎么移的么。。。看来还是没说明白,表达能力有点 ...

骚年 好好学习  天天向上 啊要{:3_52:}
作者: 赵海洋    时间: 2013-2-28 14:52
刘凯 发表于 2013-2-28 14:51
骚年 好好学习  天天向上 啊要

要要  切克闹~~~咱俩这不算刷回复吧。。。。
作者: 刘凯    时间: 2013-2-28 14:54
赵海洋 发表于 2013-2-28 14:52
要要  切克闹~~~咱俩这不算刷回复吧。。。。

不说了,要不给禁闭了     斑竹万岁 万岁 万万岁  求分 求高升 ~
作者: 袁术森    时间: 2013-2-28 15:26
刘凯 发表于 2013-2-28 14:32
计算机存储是按二进制补码形式存储的 6 在计算机中的存储是 00000000 00000000 00000000 00000110  由于 ...

恩,正解。但我对byte类型尝试的时候有点疑惑了,http://msdn.microsoft.com/zh-cn/library/x0ax6803(VS.80).aspx中的举例
  1. class  WyDemo
  2. {
  3.         public static void main(String[] args)
  4.         {
  5.             byte x=15;//x被赋值为 00001111
  6.                 x>>>=10;//无符号右移10位。实际位移2位,应得00000011=3
  7.                 System.out.println(x);
  8.         }
  9. }
复制代码
可是运行的结果是0啊 求解啊 难道我2了

位移.PNG (5.92 KB, 下载次数: 52)

位移.PNG

作者: 孙传磊    时间: 2013-2-28 15:33
本帖最后由 孙传磊 于 2013-2-28 15:40 编辑

>>> 操作符,无符号算数右移,左面补0
由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住,在表达式中过小的值总是被自动扩大为int型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。
如果变量定义为 int 型 ,那么要是 移位的次数 大于 32   就会把移位次数与32做取模,之后再做移位。
同理变量定义为 long 型 ,那么要是 移位的次数 大于 64   就会把移位次数与64做取模,之后再做移位。
下面举例说明:
例如:
  public class Yiwei {
          public static void main(String[] args)
      {
              long x=6;         //此处把int改为long   结果输出的就是 0 了
              x=x>>>32;
              System.out.println("x="+x);        
      }
}
上面的函数输出结果是 0  
若把  x=x>>>32;  中的32改成 64 那么结果又是 6 了
  现在 把上面的成员变量 x 定义为 int型
  再把  x=x>>>32;  中的32改成 33 那么结果  和  x=x>>>1;的结果是一样的
  再把  x=x>>>32;  中的32改成 34 那么结果  和  x=x>>>2;的结果是一样的
  也就是把  x=x>>>32;   和  x=x>>>0;的结果是一样的 也就是问题中的结果 6

作者: 刘凯    时间: 2013-2-28 16:26
袁术森 发表于 2013-2-28 15:26
恩,正解。但我对byte类型尝试的时候有点疑惑了,http://msdn.microsoft.com/zh-cn/library/x0ax6803(VS. ...

如楼上所说 :“由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住,在表达式中过小的值总是被自动扩大为int型。这意味着符号位扩展和移动总是发生在32位而不是8位或16位。”

你说的这个 我也不知道是个什么情况  ,, 高手解答吧
作者: 宋朋山    时间: 2013-2-28 17:04
顶12楼  int类型的>>>32等于>>>0    值不变.
作者: 薛飞    时间: 2013-2-28 17:47
如果变量定义为 int 型 , 把移位次数与32做取模,之后再做移位。
若变量定义为 long 型 ,把移位次数与64做取模,之后再做移位。
作者: 孙传磊    时间: 2013-2-28 18:12
孙传磊 发表于 2013-2-28 15:33
>>> 操作符,无符号算数右移,左面补0
由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住, ...

是的 ,我个人认为  如果 直接取模运算是不是每一次位移都要进行一次取模运算?
现在我想知道的是 如果做一次判断 (判断是否小于 32或64) 会不会在性能上
比每一次位移都要进行一次取模运算更优越一些?
作者: 赵海洋    时间: 2013-2-28 19:27
袁术森 发表于 2013-2-28 15:26
恩,正解。但我对byte类型尝试的时候有点疑惑了,http://msdn.microsoft.com/zh-cn/library/x0ax6803(VS. ...

例子里的字长是8位,15是0000 1111,右移10位相当于右移两位。你计算机字长32位,15是0000 0000 0000 0000 0000 0000 0000 1111,右移十位之后是0000 0000 0000 0000 0000 0000 0000 0000,所以是0.
作者: 赵海洋    时间: 2013-2-28 19:32
孙传磊 发表于 2013-2-28 15:33
>>> 操作符,无符号算数右移,左面补0
由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住, ...

只对32位或64位有意义?如果我机器字长,存储字长都是8位的,对8位的也可以做取模吧。这个应该跟字长有关。求解释~~
作者: 孙传磊    时间: 2013-2-28 19:35
本帖最后由 孙传磊 于 2013-2-28 19:37 编辑
刘凯 发表于 2013-2-28 16:26
如楼上所说 :“由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住,在表达式中过小的值总 ...

java里就是这样规定的,下面的例子说明了这一点:
public class ByteShift {
        public static void main(String[] args) {
                char hex[]={'0','1','2','3','4','5','6','7',
                                '8','9','A','B','C','D','E','F'};
                byte b=(byte) 0xf1;
                byte c=(byte) (b>>4);
                byte d=(byte) (b>>>4);
                byte e=(byte) ((b & 0xff)>>4);
        System.out.println("    b=0x"+hex[(b>>4) & 0x0f]+hex[b & 0x0f]);
        System.out.println("    b>>4=0x"+hex[(c>>4) & 0x0f]+hex[c & 0x0f]);
        System.out.println("    b>>>4=0x"+hex[(d>>4) & 0x0f]+hex[d & 0x0f]);
        System.out.println("(b & 0xff)>>4=0x"+hex[(e>>4) & 0x0f]+hex[e & 0x0f]);
        }
}
输出结果:
    b=0xF1
    b>>4=0xFF
    b>>>4=0xFF
(b & 0xff)>>4=0x0F
该程序的输出显示了无符号右移运算符>>>对byte型值处理时,实际上不是对byte型值直接操作,而是将其扩大到int型后再处理
作者: 孙传磊    时间: 2013-2-28 19:35
刘凯 发表于 2013-2-28 16:26
如楼上所说 :“由于无符号右移运算符>>>只是对32位和64位的值有意义。因为要记住,在表达式中过小的值总 ...

java里就是这样规定的,下面的例子说明了这一点:
public class ByteShift {
        public static void main(String[] args) {
                char hex[]={'0','1','2','3','4','5','6','7',
                                '8','9','A','B','C','D','E','F'};
                byte b=(byte) 0xf1;
                byte c=(byte) (b>>4);
                byte d=(byte) (b>>>4);
                byte e=(byte) ((b & 0xff)>>4);
        System.out.println("    b=0x"+hex[(b>>4) & 0x0f]+hex[b & 0x0f]);
        System.out.println("    b>>4=0x"+hex[(c>>4) & 0x0f]+hex[c & 0x0f]);
        System.out.println("    b>>>4=0x"+hex[(d>>4) & 0x0f]+hex[d & 0x0f]);
        System.out.println("(b & 0xff)>>4=0x"+hex[(e>>4) & 0x0f]+hex[e & 0x0f]);
        }
}
输出结果:
    b=0xF1
    b>>4=0xFF
    b>>>4=0xFF
(b & 0xff)>>4=0x0F
该程序的输出显示了无符号右移运算符>>>对byte型值处理时,实际上不是对byte型值直接操作,而是将其扩大到int型后再处理
作者: 赵海洋    时间: 2013-2-28 20:30
孙传磊 发表于 2013-2-28 19:35
java里就是这样规定的,下面的例子说明了这一点:
public class ByteShift {
        public static void main(S ...

关于当右移的运算数是byte 和short类型时,将自动把这些类型扩大为 int 型,这个确实是java规定。但是移位不是只能是32位或者64位吧?在早期的计算机,存储字长是8位或者16位的,这些移位取模时就应该是字长的值,即8或16,只对32或64的情况有意义的话应该是在32位字长的计算机,也就是现在大多数的计算机。

作者: 王俊杰    时间: 2013-2-28 20:55
本帖最后由 王俊杰 于 2013-2-28 21:13 编辑

移位操作的右操作数应当严格小于左操作数位数的值。
x是int类型,32位,进行移位操作时,移动位数应当小于32,否则这种操作可能是未定义的。

作者: 陈丽莉    时间: 2013-3-1 20:02
赵海洋 发表于 2013-2-28 14:27
这个吧,首先要明白这个原码和算术移位以及逻辑移位。
x>>>32,是指x右移32位。这个是指不带符号的算术移位 ...

负数才是以补码形式存放的  - -||
作者: 赵海洋    时间: 2013-3-1 21:42
陈丽莉 发表于 2013-3-1 20:02
负数才是以补码形式存放的  - -||

斑竹大人,正数的补码和原码是一样的,这个,可不可以认为正数和负数都是以补码形式存储的??既然一样,这样理解好记啊,嘿嘿。
作者: 刘凯    时间: 2013-3-2 13:07
陈丽莉 发表于 2013-3-1 20:02
负数才是以补码形式存放的  - -||

姐姐 ,正数的补码不就是原码么, 同意楼上观点,统统按补码记,便于记忆、   
作者: 张宁    时间: 2013-3-6 21:56


  1. 可以试试这段代码:

  2. int main(int argc, char* argv[])

  3. {

  4. int i, j;

  5. i = 0x0FFF;

  6. j = i>>32;

  7. return 0;

  8. }

  9. 你会发现j仍然等于0x0FFF,而不是期望中的0。

  10. 在编译的时候,编译器会提示(在vc6和gcc4中都一样):“shift count is too large”

  11. 在这个程序中到底发生了什么事情呢?我们来看一看这段代码的汇编代码(Debug):

  12. mov WORD PTR [ebp-8], 4095 ;11.3

  13. $LN3:

  14. movzx eax, WORD PTR [ebp-8] ;12.8

  15. movsx eax, ax ;12.8

  16. 通过这段代码我们可以看到,编译器直接把代码编译成了赋值操作,而没有做移位操作.

  17. 再看一下Release版:

  18. mov eax, 4095 ;11.2

  19. 这下更简单,直接给eax赋值了。

  20. 再看一下下面这个例子:

  21. int main(int argc, char* argv[])

  22. {

  23. short i, j;

  24. for(int k=0; k<=32; k++)

  25. {

  26. i = 0x0FFF;

  27. j = i>>k;

  28. printf("%d %d\n", i, j);

  29. }

  30. return 0;

  31. }

  32. 这时候再看一下Release版的汇编代码:

  33. .B1.2: ; Preds .B1.3 .B1.1

  34. mov ecx, ebx ;12.11

  35. mov eax, 4095 ;12.11

  36. sar eax, cl ;12.11

  37. movsx edx, ax ;12.11

  38. push edx ;13.24

  39. push 4095 ;13.24

  40. push OFFSET FLAT: ??_C@_06A@?$CFd?5?$CFd?6?$AA@ ;13.24

  41. call _printf ;13.4

  42. ; LOE ebx esi edi

  43. .B1.7: ; Preds .B1.2

  44. add esp, 12 ;13.4

  45. ; LOE ebx esi edi

  46. .B1.3: ; Preds .B1.7

  47. add ebx, 1 ;9.22

  48. cmp ebx, 32 ;9.2

  49. jle .B1.2 ; Prob 96% ;9.2

  50. 就会发现,编译器使用了sar指令。

  51. 如果把前面例子中的printf去掉,也就是:

  52. int main(int argc, char* argv[])

  53. {

  54. short i, j;

  55. for(int k=0; k<=32; k++)

  56. {

  57. i = 0x0FFF;

  58. j = i>>k;

  59. }

  60. return 0;

  61. }

  62. 这个时候,在gcc中不能编译,提示说有"unused variable",而在vc中可以顺利地编译,但我们看它生成的Rlease版汇编的时候就会发现,这两个变量运算的结果,编译器已经得知没有地方使用,就不再编译到binary code中了。

  63. 另外,我们会发现,不论在那种情况中,i>>32都等于i.

  64. 根据上述实验,我们可以得出这样的结论:

  65. 1.编译器能很好的找到程序中常量运算,然后用最简单的方式去替换它,比如前面的mov eax,4095

  66. 2.对于函数中的死代码,编译器也能很好的察觉,这些代码不会造成程序性能的下降和code size的增大。

  67. 3.这个应该是个常识,但很多C语言的书上都未提及,在C99中,对右移有这样的规定:

  68. If the value of the right operand is negative or is

  69. greater than or equal to the width of the promoted left operand, the behavior is undefined.

  70. 也就是说,对于右移大于或等于位宽的操作,或者右移负数的操作,其结果将依赖于编译器的处理和硬件指令的处理,并不唯一。

  71. 我们可以试试这个例子:

  72. int main(int argc, char* argv[])

  73. {

  74. short i, j;

  75. for(int k=-2; k<=35; k++)

  76. {

  77. i = 0x0FFF;

  78. j = i>>(32-k);

  79. printf("%d %d\n", i, j);

  80. }

  81. return 0;

  82. }

  83. 在X86上运行,当移负数位的时候,结果是0,当移大于等于32位的时候,结果同shift&31

  84. 对于上面的例子,比较保守的写法可以是:

  85. int main(int argc, char* argv[])

  86. {

  87. int i, j;

  88. for(int k=-1; k<=33; k++)

  89. {

  90. i = 0x0FFF;

  91. int shift = 32-k;

  92. j = shift<0?0:(shift>=32?0:(i>>shift));

  93. }

  94. return 0;

  95. }





  96. 在 JVM 中,>> 操作,后面的数是 int 类型的。右移32位:

  97. 如果前一个操作数是 int 类型的话,取右移位数的低 5 位,相当于右移位数与 0x1f 做了 & 运算;
  98. 如果前一个操作数是 long 类型的话,取右移位数的低 6 位,相当于右移位数与 0x3f 做了 & 运算。

  99. 正如楼主的例子,>> 32,32(100000),可以看到它的低 5 位全是“0”,与 >> 0 的结果一致。



  100. 可见这取决于编译器是如何处理的。
复制代码





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2