9

If I have the array:

{01101111,11110000,00001111} // {111, 240, 15}

The result for a 1 bit shift is:

{10110111,11111000,00000111} // {183, 248, 7}

The array size is not fixed, and the shifting will be from 1 to 7 inclusive. Currently I have the following code (which works fine):

private static void shiftBitsRight(byte[] bytes, final int rightShifts) {
   assert rightShifts >= 1 && rightShifts <= 7;

   final int leftShifts = 8 - rightShifts;

   byte previousByte = bytes[0]; // keep the byte before modification
   bytes[0] = (byte) (((bytes[0] & 0xff) >> rightShifts) | ((bytes[bytes.length - 1] & 0xff) << leftShifts));
   for (int i = 1; i < bytes.length; i++) {
      byte tmp = bytes[i];
      bytes[i] = (byte) (((bytes[i] & 0xff) >> rightShifts) | ((previousByte & 0xff) << leftShifts));
      previousByte = tmp;
   }
}

Is there a faster way to achieve this than my current approach?

mota
  • 5,275
  • 5
  • 34
  • 44
  • 3
    I think grouping into `long`s first would be beneficial for performance. – Niklas B. Mar 22 '12 at 15:32
  • If this is for graphics, another option to think about is to use a run-length encoded format. Then the shifting will not have to change all of the run lengths in the middle of the line. – BitBank Mar 22 '12 at 16:18
  • 1
    `long` might improve performance, but it will vary from machine to machine. (Sometimes `int` will be better.) – Louis Wasserman Mar 22 '12 at 16:18
  • Using a long[] can be 4x to 8x faster than using a byte[] for the same amount of data. – Peter Lawrey Mar 22 '12 at 20:29
  • Unfortunately, grouping them into longs or ints won't be beneficial in my situation, because I need to get the digest of this array after the shift, and the MessageDigest object requires a byte array, so I will need to unwrap the longs into bytes each time I finish the bit shifting. – mota Mar 23 '12 at 14:20
  • this is a **rotation**, not a shift – phuclv Jun 19 '14 at 01:29
  • @phuclv I'm not a native English speaker, but according to Wikipedia, a circular shift (or as you said, rotation) is what I was after: https://en.wikipedia.org/wiki/Circular_shift - it's all terminology anyway. I've adjusted the title to reflect this. Thanks! – mota Aug 16 '19 at 20:25
  • @Motasim yes it's correct if you include "circular" but without it "shift" in bitwise operations refers to a linear shift – phuclv Aug 17 '19 at 01:02

4 Answers4

4

The only way to find out is with thorough benchmarking, and the fastest implementations will vary from platform to platfrm. Use a tool like Caliper if you really need to optimize this.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • 3
    Downvoters, explain? (I would be very surprised if there were any single generic answer to the OP's question that was more specific than this.) – Louis Wasserman Mar 22 '12 at 16:10
1

One of the things you can do is replace (byte[a]&0xff)>>b with byte[a]>>>b

Also, you don't need &0xff when you are left shifting.

Although it may not matter, either adding final to tmp or moving the declaration out of the loop may help a tiny bit.

Another thing might try is:

int tmp=bytes[bytes.length-1];
for (int i = bytes.length-2; i >=0; i--) {
  tmp=(tmp<<8)|bytes[i];
  bytes[i] = (byte) (tmp>>>rightShifts);
 }

Then you solve bytes[bytes.length-1] afterwards.

That reverse for loop may also help if you are superstitious. I've seen it work before.

Loop Analysis per pass:

yours: 3 assignments, two shifts, one or, one cast.

mine: 2 assignments, two shifts, one or, one cast.

Rob Kielty
  • 7,958
  • 8
  • 39
  • 51
warren
  • 11
  • 1
  • `(byte[a]&0xff)>>b` and `byte[a]>>>b` are not equivalent! In the former, the left side is promoted to `int` after removing high bytes producing desired result. In the latter, the promotion occurs, then an unsigned shift so the bits to the left of `a` will be the sign bit of `a`, then `b` number of 0's. – TT-- Feb 05 '18 at 16:38
0

Use ByteBuffer.wrap to get a buffer that wraps your byte[] and then useByteBuffer.asLongBuffer() to get a view that allows you to extract and manipulate longs as suggested by @NiklasB. thus taking advantage of the hardware's ability to shift larger chunks of bits.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
0

You can generalize this to longs and more than one bit shift if you like

// for a 1-bit rightshift:
// first rotate each byte right by one
for (i = 0; i < n; i++) b[i] = rotr(b[i], 1);
// get rightmost bit
bit = b[n-1] & 0x80;
// copy high order bit from adjacent byte
for (i = n; --i >= 1;){
    b[i] &= 0x7f;
    b[i] |= (b[i-1] & 0x80);
}
// put in the rightmost bit on the left
b[0] = (b[0] & 0x7f) | bit;

assuming rotr is defined.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135