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.

+2


source to share


1 answer


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

    and short

    . You rarely need to.
  • If you work with them, always be wary of the need to mask.
  • byte

    to is char

    always difficult because:
    • Although char

      wider byte

      ...
    • char

      unsigned, but byte

      - !!!
    • Therefore, it is not just an expanding transformation, but an expanding transformation narrowing!

JLS 5.1.4 Expanding and Narrowing Primitive Conversions

The following transform combines both expansion and contraction of primitive transformations:

  • byte

    before char

    .

First, it is byte

converted to int

by extending the primitive transform, and then the resulting one is int

converted to char

by narrowing the primitive transform.


Additional links

+1


source







All Articles