0

I have a method that looks like this:

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;
    }

For this example, short ir = 1557.

The expected return value for addr is 21, but I'm getting 11 instead. My console outputs for the two sysouts read:

LeftSh Reads 11111111111111111010100000000000

RightSh Reads 1011

I'm learning Java on my own and feel that I missed something fundamental about either casting or bit-shifting that's causing this to happen. How do I fix this?

  • 1
    Please ignore the debugging() method. It prints out other values from elsewhere in the code and does not affect any of the variables in this method. – thereIwasnt Nov 08 '20 at 17:52
  • Why 11? Did you pick that number because it's the smallest shift that causes this problem? This problem is obviously hitting some limitation on the size of a `short`. – CryptoFool Nov 08 '20 at 18:08
  • https://www.tutorialspoint.com/Bitwise-right-shift-operator-in-Java – Tarik Nov 08 '20 at 18:11

3 Answers3

1

Use logical-and instead of left/right shift, to avoid issues with the fact that short is signed:

short i = 1557;

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

or, perhaps more clearly

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

To understand what is happening in your code I suggest you print out the value after right-shifting but before applying abs().

Jim Garrison
  • 85,615
  • 20
  • 155
  • 190
1

When you shift right you are getting a -11 due to resulting twos complement of the number.

When you take the absolute value you then get 11.

To get the expected 21 you need to do the following after the left shift.

addr = (short) ((addr&0xFFFF) >>> 11);
  • This masks off the 16 bits of the left shifted short.
  • then right shifts thru the sign using >>>.
System.out.println(addr);

Prints

21
WJS
  • 36,363
  • 4
  • 24
  • 39
0

The problem is that short is a signed data type. Values with the highest bit equal to 1 represent negative values:

  • Size conversions like the conversion from short to int sign-extend negative values, meaning that 16 bits of 1 are added in front of a negative short value.

  • Arithmetic shift right, for negative values, inserts 1 bits in front of the shifted pattern.

Let's follow the input of 1557 (0x0615 = 0000 0110 0001 0101) through your code:

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

As already answered by others, use bitwise And instead of the shifts.

Ralf Kleberhoff
  • 6,990
  • 1
  • 13
  • 7