V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
clearbug
V2EX  ›  Java

Java 逻辑左移运算的规则?

  •  
  •   clearbug · 2016-09-11 09:42:00 +08:00 · 3450 次点击
    这是一个创建于 2988 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    14 条回复    2016-09-11 17:23:26 +08:00
    Septembers
        1
    Septembers  
       2016-09-11 11:08:36 +08:00
    imn1
        2
    imn1  
       2016-09-11 11:25:08 +08:00
    位运算?
    x <<< n 相当于 x * 2^n
    clearbug
        3
    clearbug  
    OP
       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
        4
    clearbug  
    OP
       2016-09-11 15:26:55 +08:00
    @imn1 不止这么简单的运算吧,我想搞明白内部运算规则
    Septembers
        5
    Septembers  
       2016-09-11 15:58:29 +08:00
    @clearbug 我主力不是写 Java 因此无法为您回答
    clearbug
        6
    clearbug  
    OP
       2016-09-11 16:22:57 +08:00
    @Septembers 。。。好吧。谢谢提供官方文档哈。敢问大神你主力是啥呢?
    1023400273
        7
    1023400273  
       2016-09-11 16:35:45 +08:00
    @imn1 Java 只有>>>运算符没有<<<运算符吧?
    imn1
        8
    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
        9
    imn1  
       2016-09-11 16:50:44 +08:00
    @1023400273
    不懂 java ,不过我上面 2L 写错了,移位运算符是<<和>>,不是<<<,>>>
    clearbug
        11
    clearbug  
    OP
       2016-09-11 17:11:55 +08:00
    @oldwolf 大神威武,把 jvm 的实现都搬出来了。。。可惜对 cpp 不太懂。。顺便问一下:你学 Java 都直接看 jvm 实现吗?
    clearbug
        12
    clearbug  
    OP
       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
        13
    napsterwu  
       2016-09-11 17:18:25 +08:00
    Java 有 左移<< 右移>> 无符号右移>>> 以上就是关键词了
    clearbug
        14
    clearbug  
    OP
       2016-09-11 17:23:26 +08:00
    @napsterwu 嗯嗯是的。因为搞一个无符号左移<<<出来没啥意义,符号位就在左边。结合答友贴出的官方文档和 sf 中第一个答友的详细解答,基本上已经搞明白了。感谢各位了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1662 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 16:56 · PVG 00:56 · LAX 08:56 · JFK 11:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.