Reputation: 87
So I understand how to change an individual bit within a byte, what I am not sure is why my particular code is not working.
public static void setBit(byte[] input, int position, int value) {
int byteLocation = position / 8;
int bitLocation = position % 8;
byte tempByte = input[byteLocation];
if (value == 0)
tempByte = (byte) (tempByte & ~(1 << bitLocation));
else
tempByte = (byte) (tempByte | (1 << bitLocation));
input[byteLocation] = tempByte;
}
Now I have been testing it with the string "Testing1" which is 64bits long, then attempting to set the bits and display the value. It works a treat up to 46 bits, then on the 47th bit if I attempt to set it to 1 it borks up, works fine with 0 however.
Can't see the error in my ways, here's how I am testing it
String test = "Testing1";
byte[] bytes = test.getBytes();
for (int i = 0; i < bytes.length; i++)
System.out.print(String.format("%8s", Integer.toBinaryString(bytes[i])).replace(' ', '0') + "[" + i + "] ");
setBit(bytes, 44, 1);
System.out.println();
for (int i = 0; i < bytes.length; i++)
System.out.print(String.format("%8s", Integer.toBinaryString(bytes[i])).replace(' ', '0') + "[" + i + "] ");
The following is the output when I attempt to change the 47th bit to a 1
01010100[0] 01100101[1] 01110011[2] 01110100[3] 01101001[4] 01101110[5] 01100111[6] 00110001[7]
01010100[0] 01100101[1] 01110011[2] 01110100[3] 01101001[4] 11111111111111111111111111101110[5] 01100111[6] 00110001[7]
Upvotes: 4
Views: 3883
Reputation: 793
Ive recently had to do something like this.
I managed to achieve it through (a lot of use of the whiteboard but..) shifting the original bits right by the position of the LSB I wanted to replace and making all bits inclusive of the MSB I wanted to replace, 1's.
I then AND'ed the bits I want in place of the bits I want to replace, shifted left the same number I shifted right, OR'ing the result with the original and AND'ed by an XOR'ed mask of the replacement. (Take a breath, I'll try to explain)
Let's say I have the bytes:
1111 1010 0001 1001
and I want to replace the nibble 1010 with 0001 to produce:
1111 0001 0001 1001.
The operation I went through to achieve this is:
1) Shift right by 8 to produce:
0000 0000 1111 1010
2) OR a mask of 0xf (1111) to produce:
0000 0000 1111 1111
3) AND the replacement of 0001 with 0000 0000 1111 1111 to produce:
0000 0000 0000 0001
4) Shift left by 8 to produce:
0000 0001 0000 0000
5) Shift the mask by the LSB position and XOR with full bytes
1111 1111 1111 1111
0000 1111 0000 0000
==================
1111 0000 1111 1111
6) AND the XOR'ed, shifted mask with the original to produce:
1111 0000 0001 1001
1111 0000 1111 1111
==================
1111 0000 0001 1001
7) OR that result of the above with the replacement:
1111 0000 0001 1001
0000 0001 0000 0000
==================
1111 0001 0001 1001 << end result
==================
In java, this results in the function:
public long overwriteBits(long overwrite, long with, long shift, long mask)
{
return ((((overwrite >> shift) | mask) & with) << shift) | (overwrite & (~0 ^ (mask << shift)));
}
Where "overwrite" is the original data, "with" are the bits you want in place of the bits in position "shift" and mask is a a series of positive bits with the same length of the replacement.
To do the above I'd call (in sudo):
overwriteBits(1111101000011001, 0001, 8, 1111)
I want to mention that the above will work for replacing bits in any primitive, no need for byte arrays. e.g. Replacing 11 bits, as below:
1101001010101101 1111 0101 101 001101
with 1010 1010 101
overwriteBits(1101001010101101 1111 0101 101 001101, 1010 1010 101, 6, 11111111111)
1101001010101101 1111 0101 101 001101
1101001010101101 1010 1010 101 001101
overwriteBits(1789785421l, 1365l, 6, 0x7FF)
Upvotes: 0
Reputation: 62789
I haven't looked at it in too much detail but I think the problem with the one bite is that it's being extended to an int (Since it's signed, the 1 extends to a negative int).
Just take the last 8 characters of the string and it will work fine.
Upvotes: 0
Reputation: 136112
Change formatting as
Integer.toBinaryString(0xFF & bytes[i])
byte needs to be masked off because it is sign-extended, not zero-extended, to 32-bit int
Upvotes: 3
Reputation: 33351
The problem is you are setting the sign bit in the byte in question. So, that byte now has a negative value. You call Integer.toBinaryString()
, which takes an int as it's argument, not a byte. The byte get promoted to an int, and it correctly evaluates the value of:
11101110
to it's equivalent integer:
11111111111111111111111111101110
Upvotes: 2
Reputation: 21793
I made your method smaller using ^
(xor)
public static void setBit(byte[] input, int position, int value) {
int byteLocation = position / 8;
int bitLocation = position % 8;
input[byteLocation] = (byte) (input[byteLocation] ^ (byte) (1 << bitLocation));
}
Upvotes: 0