Problems with reversing bit shifts that roll over the maximum byte size?
I am trying to do a simple cesarean shift on a binary string and it should be reversible. I did it using this method.
public static String cShift(String ptxt, int addFactor)
{
String ascii = "";
for (int i = 0; i < ptxt.length(); i+=8)
{
int character = Integer.parseInt(ptxt.substring(i, i+8), 2);
byte sum = (byte) (character + addFactor);
ascii += (char)sum;
}
String returnToBinary = convertToBinary(ascii);
return returnToBinary;
}
This works great in some cases. However, I think that when it flips, being representable by one byte, it is irreversible. In the test line "test!22*F "
with addFactor
of, the 12
line becomes irreversible. Why is this and how can I stop it?
edit: For clarification, the test string is converted to binary before being passed. This is where convertToBinary is executed
public static String convertToBinary(String str)
{
char [] array = str.toCharArray();
String binaryToBeReturned = "";
for (int i = 0; i < str.length(); i++)
{
String binary = Integer.toBinaryString((int)array[i]);
binary = padZeroes(binary);
binaryToBeReturned += binary;
}
return binaryToBeReturned;
}
When I run this with cShift 12 followed by cShift -12 for reverse, I get this ...
01110100011001010111001101110100001000010011001000110010010001100010101000100000
111111111000000001110001011111111111111110000000001011010011111000111110010100100011011000101100
ÿ?qÿ?->>R6,
ÿótesÿót!22F*
The first line just converts the test string to binary. The second line is the binary output of cShift. The third line is the result of converting this to ascii, and the fourth line is the result of calling -12 to cShift and converting to ascii.
It's very clear to me that some things are being added from the cast and I'm not really sure how to deal with that. Thanks.
source to share
You need to mask byte
when expanding to char
, as otherwise the sign bit will be expanded.
ascii += (char)(sum & 0xFF)
This masking pattern applies to signed numeric type expansion if you do not want sign expansion.
anInt = aByte & 0xFF;
anInt = aShort & 0xFFFF;
aLong = anInt & 0xFFFFFFFFL; // notice the L
Here's an example to illustrate:
byte b = -1; // 0xFF
char ch = (char) b; // 0xFFFF
int i = ch;
System.out.println(i); // prints "65535", which is 0xFFFF
byte b = -1; // 0xFF
char ch = (char) (b & 0xFF); // 0xFF
int i = ch;
System.out.println(i); // prints "255", which is 0xFF
There is a lesson here. If you've read Java Puzzlers , you'll see a few that revolve around the hooplas extension extensions. This book puzzle is essentially the same as mine above, but perhaps more confusing:
// Java Puzzlers, Puzzle 6: Multicast
System.out.println((int) (char) (byte) -1); // prints 65535
There are two ways to fix this:
- Avoid working with
byte
andshort
. You rarely need to. - If you work with them, always be wary of the need to mask.
-
byte
to ischar
always difficult because:- Although
char
widerbyte
... -
char
unsigned, butbyte
- !!! - Therefore, it is not just an expanding transformation, but an expanding transformation narrowing!
- Although
JLS 5.1.4 Expanding and Narrowing Primitive Conversions
The following transform combines both expansion and contraction of primitive transformations:
byte
beforechar
.First, it is
byte
converted toint
by extending the primitive transform, and then the resulting one isint
converted tochar
by narrowing the primitive transform.
Additional links
source to share