【问题标题】:Java Method returns wrong value after left and right arithmetic bit-shiftingJava方法在左右算术位移后返回错误值
【发布时间】:2020-11-08 17:50:00
【问题描述】:

我有一个看起来像这样的方法:

private short getAddress(short ir) { //performs bit shifting to isolate and find address
        short addr = ir;
        addr = (short) (addr << 11); //remove leading bits
        if (debug == true) {
            System.out.println("LeftSh Reads " + Integer.toBinaryString(addr));
            debugging();
        }
        addr = (short) Math.abs((addr >> 11)); //Shift back to starting position. Java's casting leaves this in negative for some reason. Taking the absolute value fixes it.
        if (debug == true) {
            System.out.println("RightSh Reads " + Integer.toBinaryString(addr));
            debugging();
        }
        return addr;
    }

对于本例,短 ir = 1557。

addr 的预期返回值为 21,但我得到的是 11。 我的两个系统输出的控制台输出如下:

LeftSh Reads 11111111111111111010100000000000

RightSh Reads 1011

我正在自学 Java,我觉得我错过了导致这种情况发生的有关强制转换或位移的一些基本知识。我该如何解决这个问题?

【问题讨论】:

  • 请忽略debugging()方法。它从代码的其他地方打印出其他值,并且不影响此方法中的任何变量。
  • 为什么是 11?你选择那个数字是因为它是导致这个问题的最小变化吗?这个问题显然对short 的大小造成了一些限制。

标签: java bit-shift short twos-complement absolute-value


【解决方案1】:

使用逻辑与而不是左/右移位,以避免short 已签名这一事实出现问题:

short i = 1557;

short i_masked = (short)(i & 0x001F);

或者,也许更清楚

short i_masked = (short) (i & 0b0000000000011111);

要了解你的代码中发生了什么,我建议你在右移之后打印出值,但应用abs()之前。

【讨论】:

    【解决方案2】:

    当您向右移动时,由于结果是数字的二进制补码,您得到 -11。

    当你取绝对值时,你会得到 11。

    要获得预期的 21,您需要在左移后执行以下操作。

    addr = (short) ((addr&0xFFFF) >>> 11);
    
    • 这会屏蔽左移短的 16 位。
    • 然后使用 >>> 通过符号右移。
    System.out.println(addr);
    

    打印

    21
    

    【讨论】:

    • 在所有现代 CPU 中都是二进制补码,而不是一个补码。
    • 糟糕!我的意思是二进制补码。谢谢!
    【解决方案3】:

    问题在于short 是有符号数据类型。最高位等于 1 的值表示负值:

    • 大小转换,如从shortint 的转换符号扩展负值,这意味着1 的16 位被添加到负short 值的前面。

    • 算术右移,对于负值,在移位模式前插入 1 位。

    让我们通过您的代码跟踪 1557 (0x0615 = 0000 0110 0001 0101) 的输入:

    addr = (short) (addr << 11);           // Gives 10101 00000000000, a negative number.
    Integer.toBinaryString(addr)           // Sign-extends to "11111111111111111010100000000000"
    addr = (short) Math.abs((addr >> 11)); // (addr >> 11) gets sign-extended: 
                                           // 11111111111 10101, meaning -11
                                           // abs() gives 00000000000 01011, meaning 11
    

    正如其他人已经回答的那样,请使用按位 And 而不是移位。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-25
      相关资源
      最近更新 更多