Java 逻辑左移运算的规则?

2016-09-11 09:42:00 +08:00
 clearbug

我在 segmentfault 上提了一个关于 Java 逻辑左移运算的规则的问题,希望得到完善的解答,链接: https://segmentfault.com/u/clearbug

3306 次点击
所在节点    Java
14 条回复
Septembers
2016-09-11 11:08:36 +08:00
imn1
2016-09-11 11:25:08 +08:00
位运算?
x <<< n 相当于 x * 2^n
clearbug
2016-09-11 15:25:59 +08:00
@Septembers 英语不太好,文档终于阅读完了。看文档里有这么一句:
“ At run time, shift operations are performed on the two's-complement integer representation of the value of the left operand.”也就是说在运行时,移位操作时左操作数使用的是左操作数的补码进行移位运算的,然后运行完再将结果转换为原码就可以知晓其具体数值了( PS :不知道这里理解对否。。)。
那么我想问一下:
int x = 3; //00000000,00000000,00000000,00000011 (补码和原码是一样的)
int x31 = x << 31;
1.求 x31 的运算过程:
00000000,00000000,00000000,00000011->
10000000,00000000,00000000,00000000 (移位运算,右边补零)
这个运算过程正确吗?如果正确的话,结果 10000000,00000000,00000000,00000000 又该怎么转换成为原码呢?
clearbug
2016-09-11 15:26:55 +08:00
@imn1 不止这么简单的运算吧,我想搞明白内部运算规则
Septembers
2016-09-11 15:58:29 +08:00
@clearbug 我主力不是写 Java 因此无法为您回答
clearbug
2016-09-11 16:22:57 +08:00
@Septembers 。。。好吧。谢谢提供官方文档哈。敢问大神你主力是啥呢?
1023400273
2016-09-11 16:35:45 +08:00
@imn1 Java 只有>>>运算符没有<<<运算符吧?
imn1
2016-09-11 16:48:43 +08:00
@clearbug
我也不懂 java ,不过这个跟 java 无关,是位运算知识

就是这么简单,只不过位运算有位限制(默认 32 位或 64 位),你上面的例子最左边的 1 左移 31 位后溢出了
另外符号问题,第 32 位为 1 时可能是负数
有些语言会有点不同,例如 php 根据机器默认 32 或 64 , python 一般情况下乘法加法不会溢出,但少数情况如取反操作会限制位数

110000000000000000000000000000000(2 进制共 33 位)=6442450944(10 进制)=3*2147483648=3*2^31
右移就是舍弃右边,相当于除以 2^n 并取整数部分(舍弃余数)

左移如果没有溢出可以通过右移相同位复原,右移除非去掉的全是二进制 0 ,否则无法复原

其实编译时,编译器就是把一些计算换成位运算的
如十进制 3*3 ,就是 3*(2+1) => (3<< 1) + (3<<0)
有些非编译语言也可以这样写起到加速作用
imn1
2016-09-11 16:50:44 +08:00
@1023400273
不懂 java ,不过我上面 2L 写错了,移位运算符是<<和>>,不是<<<,>>>
oldwolf
2016-09-11 16:53:59 +08:00
clearbug
2016-09-11 17:11:55 +08:00
@oldwolf 大神威武,把 jvm 的实现都搬出来了。。。可惜对 cpp 不太懂。。顺便问一下:你学 Java 都直接看 jvm 实现吗?
clearbug
2016-09-11 17:14:25 +08:00
@imn1 我比较菜。。你说得我也不太懂,不过 sf 上第一个回答我感觉倒是挺合理:

着重讲一下你不理解的 10000000,00000000,00000000,00000000 怎么取补码(也就是从补码转原码)的过程吧。
取补的过程你也很清楚了,但是有一个关键点,就是在+1 的过程中不会改变符号位。在通常的情况下,+1 都不会影响符号位,只有两个数字比较特别,就是 00000000,00000000,00000000,00000000 和 10000000,00000000,00000000,00000000 。这两个取反码是 01111111,11111111,11111111,11111111 和 11111111,11111111,11111111,11111111 ,如果直接+1 都会影响符号位。而真正的去补过程是不修改符号位的,所以这两个数的补码计算之后仍然为他们本身。 所以补码 10000000,00000000,00000000,00000000 的源码就是 10000000,00000000,00000000,00000000 ,也就是负数的最大值 -2147483648

因为没有-0 ,所以 10000000,00000000,00000000,00000000 就是负数的最大值 -2147483648 。。记得之前教科书上好像也说过这个
napsterwu
2016-09-11 17:18:25 +08:00
Java 有 左移<< 右移>> 无符号右移>>> 以上就是关键词了
clearbug
2016-09-11 17:23:26 +08:00
@napsterwu 嗯嗯是的。因为搞一个无符号左移<<<出来没啥意义,符号位就在左边。结合答友贴出的官方文档和 sf 中第一个答友的详细解答,基本上已经搞明白了。感谢各位了。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/305401

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX