请选择 进入手机版 | 继续访问电脑版
楼主: yujianta14
680 0

大数据Java基础——移位运算的真实剖析 (一) [推广有奖]

  • 0关注
  • 0粉丝

本科生

36%

还不是VIP/贵宾

-

威望
0
论坛币
130 个
通用积分
0
学术水平
0 点
热心指数
0 点
信用等级
0 点
经验
1703 点
帖子
45
精华
0
在线时间
10 小时
注册时间
2016-4-18
最后登录
2016-6-27

yujianta14 发表于 2016-5-11 16:30:13 |显示全部楼层 |坛友微信交流群

+2 论坛币
k人 参与回答

经管之家送您一份

应届毕业生专属福利!

求职就业群
赵安豆老师微信:zhaoandou666

经管之家联合CDA

送您一个全额奖学金名额~ !

感谢您参与论坛问题回答

经管之家送您两个论坛币!

+2 论坛币
  抛砖引玉:
  Java 中定义了 3 种移位运算符,分别是左移运算符“<<”、右移运算符“>>”和无符号右移运算符“>>>”,对于移位运算,移位运算两边的操作数要求为整型,即 byte、short、char、 int 和 long类型,或者通过拆箱转换后为整型。当操作数的类型为 byte、short 或 char 类型时, 会自动提升为 int 类型,运算的结果也为 int类型。对于移位运算,人们对其“误会”实在太深了……
  超过自身位数的移位
  我们知道,int 类型占用 4 字节,32 位,而 long 类型占用 8 字节,64 位。那么,如果将 int 类型(long 类型)移动超过 31位(63 位)便失去了意义,因为用通俗的话来说,就是“全移 走了”。不过幸运的是,当左侧操作数为 int 类型(byte、char 与 short类型自动提升为 int 类型) 或 long 类型时,如果右侧操作数大于 31 或 63,系统做了相关处理。
  是怎么处理的呢?普遍都是这样认为的:如果左侧操作数为 int 类型(包括提升后为 int 类 型),会对右侧操作数进行除数为 32 的求余运算,如果左侧操作数为long 类型,会对右侧操作 数进行除数为64的求余运算,例如:

  1.  int i = 20;
  2.   int j = 30;
  3.   i = i << 3;
  4.   j = j >> 70;
复制代码

  结果会先进行求余运算:

  1. 3 % 32 //结果为3
  2.   70 % 64 //结果为6
复制代码

  因此,实际右侧的操作数是 3 与 6,而不是 3 与 70。
  90%的 Java 程序员都持上述观点,然而,遗憾的是,这个想法是不正确的。
  需要注意的是:右侧的操作数可以是任意的整型数值,只要该值没有超过类型变量的取值 范围就可以。那么,当右侧操作数为负值时,例如:

  1.  int i= 28;
  2.   i = 28 << -4;
复制代码

  按照上面的观点,首先对 32 求余,即:

  1. -4 % 32
复制代码

  结果还是−4,现在问题来了,向左移动−4 位,该怎样移动?
  实际上,当左侧操作数为 int 类型时(包括提升后为 int 类型),右侧操作数只有低5位是有效的(低 5 位的范围为 0~31) ,也就是说可以看作右侧操作数会先与掩码 0x1f 做与运算(&) ,然后左侧操作数再移动相应的位数。类似地,当左侧操作数为 long 类型时,右侧操作数只有低 6 位是有效的,可以看作右侧操作数先与掩码 0x3f做与运算,然后再移动相应的位数。例如, 假设有如下的赋值运算:

  1. int i = 5 << -10;
  2.   −10 的补码为: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 0
复制代码

  取其低 5 位,结果为: 1 0 1 1 0 这个值就是 22,也就是相当于:

  1. int i = 5 << 22;
复制代码

  因此,不要把移位运算右侧的操作数与求余运算联系在一起,那是不正确的。
  移位运算与乘除运算
  由于数据采用二进制来表示,因此就会普遍存在这样的想法:左移一位就相当于乘以 2, 而右移一位就相当于除以 2,这种想法正确吗?下面的程序将给予验证。
  【例 】移位与乘除

  1. 1. package chapter2;
  2.   2.
  3.   3. public class ShiftOperation {
  4.   4. public static void main(String[] args) {
  5.   5. testShift(10); //正偶数
  6.   6. testShift(-10); //负偶数
  7.   7. testShift(0); //0
  8.   8. testShift(9); //正奇数
  9.   9. testShift(-9); //负奇数
  10.   10. }
  11.   11.
  12.   12. public static void testShift(int value) {
  13.   13. int multiply = value * 2;
  14.   14. int divide = value / 2;
  15.   15. int leftShift = value << 1;
  16.   16. int rightShift = value >> 1;
  17.   17. System.out.println(value + "*2=" + multiply);
  18.   18. System.out.println(value + "/2=" + divide);
  19.   19. System.out.println(value + "<<1=" + leftShift);
  20.   20. System.out.println(value + ">>1=" + rightShift);
  21.   21. System.out.print("测试结果:" + value + "*2="+ value + "<<1"); 22. if (multiply == leftShift) {
  22.   23. System.out.println("通过!");
  23.   24. } else {
  24.   25. System.out.println("不通过!");
  25.   26. }
  26.   27. System.out.print("测试结果:" + value + "/2="+ value + ">>1"); 28. if (divide == rightShift) {
  27.   29. System.out.println("通过!");
  28.   30. } else {
  29.   31. System.out.println("不通过!");
  30.   32. }
  31.   33. }
  32.   34.
复制代码

  本程序选取了几个数值,然后分别计算该数值乘以 2、除以 2、左移一位与右移一位的值(第 13~16 行),接下来比较乘以 2 与左移一位(第22~26 行)、除以 2 与右移一位(第 28~32 行) 是否相等。程序运行结果如下:

  1.  10*2=20
  2.   10/2=5
  3.   10<<1=20
  4.   10>>1=5
  5.   测试结果:10*2=10<<1通过!
  6.   测试结果:10/2=10>>1通过!
  7.   -10*2=-20
  8.   -10/2=-5
  9.   -10<<1=-20
  10.   -10>>1=-5
  11.   测试结果:-10*2=-10<<1通过!
  12.   测试结果:-10/2=-10>>1通过!
  13.   0*2=0
  14.   0/2=0
  15.   0<<1=0
  16.   0>>1=0
  17.   测试结果:0*2=0<<1通过!
  18.   测试结果:0/2=0>>1通过!
  19.   9*2=18
  20.   9/2=4
  21.   9<<1=18
  22.   9>>1=4
  23.   测试结果:9*2=9<<1通过!
  24.   测试结果:9/2=9>>1通过!
  25.   -9*2=-18
  26.   -9/2=-4
  27.   -9<<1=-18
  28.   -9>>1=-5
  29.   测试结果:-9*2=-9<<1通过!
  30.   测试结果:-9/2=-9>>1不通过!
复制代码

  输出结果除了最后一行以外,其余都是相等的。那么最后一行有什么特别吗? 这要从相除的舍入模式说起。在 Java中,当两个操作数都是整型的时候,结果也是整型的 (假设没有发生 ArithmeticException 异常)。如果不能整除,则结果是向 0 舍入的,也就是说,向靠近 0 的方向取值。例如在本程序中,表达式:

  1.  9 / 2
复制代码

  的值为 4.5,介于 4 与 5 之间,由于 4 更靠近 0,所以向 0 舍入,结果为 4,这相当于向下舍入, 而表达式:

  1. -9 / 2
复制代码

  的值为−4.5,介于−4 与−5 之间,向 0 舍入为−4,这相当于向上舍入。而对于移位运算来说,表达式:

  1.  9 >> 1
复制代码

  的值为 4,相当于向下舍入,而表达式:

  1.  -9 >> 1
复制代码

  的值为−5,还是相当于向下舍入,因此,不同的数值出现了。由于相乘运算与整除的时候不涉及如上舍入模式的问题,所以,值是相等的。但是,一旦不能整除,就会涉及舍入模式,这样, 结果就会存在差异了。表 2-4 给出了具体的情况。
  所以,乘以 2n与左移 n 位的值是相等的,如果可以整除,除以 2n与右移 n 位的值也是相等 的。如果不能整除,当被除数为正数时,除以 2n与右移n 位的值相等,当被除数为负数时,除 以 2n与右移 n 位的值则是不相等的。
  注意 以上所指的右移,如无特殊说明,均指有符号右移(>>)。
  本文出自柠檬派 ,作者:梁勇http://www.lemonpai.com/1009.html 请务必保留此出处 ,否则将追究法律责任!
  相关阅读:大数据Java基础——移位运算的真实剖析 (二) http://www.lemonpai.com/1023.html 有问题希望大家多多指点。

二维码

扫码加我 拉你入群

请注明:姓名-公司-职位

以便审核进群资格,未注明则拒绝

关键词:java基础 Java jav 大数据 Short Java 幸运

您需要登录后才可以回帖 登录 | 我要注册

本版微信群
加JingGuanBbs
拉您进交流群

京ICP备16021002-2号 京B2-20170662号 京公网安备 11010802022788号 论坛法律顾问:王进律师 知识产权保护声明   免责及隐私声明

GMT+8, 2024-3-29 17:34