sof
sof

Reputation: 9649

Bits retrieval of a number

Tried to retrieve some bits of a number, e.g. the marked bits 00001011 of the byte 11 below,

(byte) 11 >> 1 << 6 >> 5

but why is the result 10 instead of 2 ?

@EDIT

To make a method below, the solution from @Yassin Hajaj seems more feasible.

public byte getBits(byte b, int from, int to) { // from & to inclusive

  return (byte) (b >> from << (8 - (to - from + 1))) >> (8 - to - 1);
}

getBits((byte) 11, 1, 2); // => 2

Or more universal with the hints from @Andreas,

public <T extends Number> long getBits(T n, int from, int to) {

  return n.longValue() >>> from << (64 - (to - from + 1)) >>> (64 - to - 1);
}

getBits((byte) 11, 1, 2); // => 2

Upvotes: 3

Views: 302

Answers (4)

Yassin Hajaj
Yassin Hajaj

Reputation: 21965

Another way of getting 2 is to force the cast to byte after having reached << 6 to keep only the last eight bits of the number.

public static void main(String[] args) {
    byte b;
    b = (byte) (11 >> 1 << 6) >> 5;
    System.out.println(b); // 2
}

Upvotes: 3

Andreas
Andreas

Reputation: 159086

TL;DR Use b & 6, e.g. (byte)(11 & 6). See working getBits() implementation(s) at the end.


First of all, casting 11 to a byte is rather meaningless, because the >> operator will coerce it right back to an int value.

To show you why your code doesn't work, here is a program that displays all the intermediate steps:

public static void main(String[] args) {
    for (byte i = 0; i <= 16; i++) {
        int i1 = i >> 1;
        int i2 = i1 << 6;
        int i3 = i2 >> 5;
        System.out.printf("%3d %s -> %3d %s -> %3d %10s -> %3d %s%n", i, bin(i), i1, bin(i1), i2, bin(i2), i3, bin(i3));
    }
}
private static String bin(int value) {
    String s = Integer.toBinaryString(value);
    return "0000000".substring(Math.min(7, s.length() - 1)) + s;
}

Output:

  0 00000000 ->   0 00000000 ->   0   00000000 ->   0 00000000
  1 00000001 ->   0 00000000 ->   0   00000000 ->   0 00000000
  2 00000010 ->   1 00000001 ->  64   01000000 ->   2 00000010
  3 00000011 ->   1 00000001 ->  64   01000000 ->   2 00000010
  4 00000100 ->   2 00000010 -> 128   10000000 ->   4 00000100
  5 00000101 ->   2 00000010 -> 128   10000000 ->   4 00000100
  6 00000110 ->   3 00000011 -> 192   11000000 ->   6 00000110
  7 00000111 ->   3 00000011 -> 192   11000000 ->   6 00000110
  8 00001000 ->   4 00000100 -> 256  100000000 ->   8 00001000
  9 00001001 ->   4 00000100 -> 256  100000000 ->   8 00001000
 10 00001010 ->   5 00000101 -> 320  101000000 ->  10 00001010
 11 00001011 ->   5 00000101 -> 320  101000000 ->  10 00001010
 12 00001100 ->   6 00000110 -> 384  110000000 ->  12 00001100
 13 00001101 ->   6 00000110 -> 384  110000000 ->  12 00001100
 14 00001110 ->   7 00000111 -> 448  111000000 ->  14 00001110
 15 00001111 ->   7 00000111 -> 448  111000000 ->  14 00001110
 16 00010000 ->   8 00001000 -> 512 1000000000 ->  16 00010000

Your upper bits are not getting cleared, because it's operating on int values. If you change everything to byte, you get:

public static void main(String[] args) {
    for (byte i = 0; i <= 16; i++) {
        byte i1 = (byte)(i >> 1);
        byte i2 = (byte)(i1 << 6);
        byte i3 = (byte)(i2 >> 5);
        System.out.printf("%3d %s -> %3d %s -> %4d %s -> %3d %s%n", i, bin(i), i1, bin(i1), i2, bin(i2), i3, bin(i3));
    }
}
private static String bin(byte value) {
    String s = Integer.toBinaryString(value & 0xFF);
    return "0000000".substring(s.length() - 1) + s;
}
  0 00000000 ->   0 00000000 ->    0 00000000 ->   0 00000000
  1 00000001 ->   0 00000000 ->    0 00000000 ->   0 00000000
  2 00000010 ->   1 00000001 ->   64 01000000 ->   2 00000010
  3 00000011 ->   1 00000001 ->   64 01000000 ->   2 00000010
  4 00000100 ->   2 00000010 -> -128 10000000 ->  -4 11111100
  5 00000101 ->   2 00000010 -> -128 10000000 ->  -4 11111100
  6 00000110 ->   3 00000011 ->  -64 11000000 ->  -2 11111110
  7 00000111 ->   3 00000011 ->  -64 11000000 ->  -2 11111110
  8 00001000 ->   4 00000100 ->    0 00000000 ->   0 00000000
  9 00001001 ->   4 00000100 ->    0 00000000 ->   0 00000000
 10 00001010 ->   5 00000101 ->   64 01000000 ->   2 00000010
 11 00001011 ->   5 00000101 ->   64 01000000 ->   2 00000010
 12 00001100 ->   6 00000110 -> -128 10000000 ->  -4 11111100
 13 00001101 ->   6 00000110 -> -128 10000000 ->  -4 11111100
 14 00001110 ->   7 00000111 ->  -64 11000000 ->  -2 11111110
 15 00001111 ->   7 00000111 ->  -64 11000000 ->  -2 11111110
 16 00010000 ->   8 00001000 ->    0 00000000 ->   0 00000000

Here, the problem is the sign-extension you get from >>. Even switching to >>> won't work, because the >>> still coerces to int with sign-extension before the shift happens.

To get rid of sign-extension, you have to convert byte to int using b & 0xFF, because the & will coerce b to an int with sign-extension, then the bitwise AND operator will remove all those bits.

Of course, if you're going to use bitwise AND anyway, just use it to get the desired result, i.e. b & 0b00000110 (or b & 6).


For the same reason as described above, the getBits() method doesn't work.

Solution is to still use a bitwise AND operator, but construct the bit-mask from the supplied from and to values.

The trick here is the use (1 << x) - 1 to create a mask of x bits, e.g. 5 -> 0b00011111. So if you want from 2 to 4 inclusive, build 0x00011111 (5! bits) and 0x00000011 (2 bits), then XOR them to get 0x00011100.

public static byte getBits(byte b, int from, int to) {
    if (from < 0 || from > to || to > 7)
        throw new IllegalArgumentException();
    int mask = ((1 << (to + 1)) - 1) ^ ((1 << from) - 1);
    return (byte)(b & mask);
}
  0 00000000 ->   0 00000000
  1 00000001 ->   0 00000000
  2 00000010 ->   2 00000010
  3 00000011 ->   2 00000010
  4 00000100 ->   4 00000100
  5 00000101 ->   4 00000100
  6 00000110 ->   6 00000110
  7 00000111 ->   6 00000110
  8 00001000 ->   0 00000000
  9 00001001 ->   0 00000000
 10 00001010 ->   2 00000010
 11 00001011 ->   2 00000010
 12 00001100 ->   4 00000100
 13 00001101 ->   4 00000100
 14 00001110 ->   6 00000110
 15 00001111 ->   6 00000110
 16 00010000 ->   0 00000000

For other primitive types, overload the method:

public static byte getBits(byte value, int from, int to) {
    if (from < 0 || from > to || to > 7)
        throw new IllegalArgumentException();
    int mask = ((1 << (to + 1)) - 1) ^ ((1 << from) - 1);
    return (byte)(value & mask);
}
public static short getBits(short value, int from, int to) {
    if (from < 0 || from > to || to > 15)
        throw new IllegalArgumentException();
    int mask = ((1 << (to + 1)) - 1) ^ ((1 << from) - 1);
    return (short)(value & mask);
}
public static int getBits(int value, int from, int to) {
    if (from < 0 || from > to || to > 31)
        throw new IllegalArgumentException();
    int mask = ((1 << (to + 1)) - 1) ^ ((1 << from) - 1);
    return value & mask;
}
public static long getBits(long value, int from, int to) {
    if (from < 0 || from > to || to > 63)
        throw new IllegalArgumentException();
    long mask = ((1L << (to + 1)) - 1) ^ ((1L << from) - 1); // <-- notice the change to long and 1L
    return value & mask;
}

Upvotes: 2

infixed
infixed

Reputation: 1155

So you have 11 base 10 ( 0b1011 aka 0x0B ) and you want the 2's bit and the 4's bit?

Just do a bitwise AND with those two bits ( 0x04 + 0x02 = 0x06 )

x = 11 & 0x06;

Shifting not required

Upvotes: 2

Michael
Michael

Reputation: 2773

You did not specify your initial number to be of the binary system. Taking advantage of binary literals, you could fix this by prefixing the number with 0b. The code then outputs the desired 2

byte b = (byte) 0b11 >> 1 << 6 >> 5;
System.out.println(b); // 2

Upvotes: 6

Related Questions