Reputation: 9649
Tried to retrieve some bits of a number, e.g. the marked bits 0000101
1 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
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
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
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
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