Anonymouss121
Anonymouss121

Reputation: 33

Java shift right outputting negative value

i'm really confused with, well it's best if I show my code first so here's what I got.

void dumpInt(int x) throws IOException {
    if ( true ) {
        //8388638
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        writer.writeByte(x&0xff);
        writer.writeByte((x>>8)&0xff);
        writer.writeByte((x >> 16) & 0xfff);
        writer.writeByte((x>>24)&0xff);
        outputStream.write(x & 0xff);
        outputStream.write((x >> 8) & 0xff);
        outputStream.write((x >> 16) & 0xff);
        outputStream.write((x >> 24) & 0xff);
        System.out.println((x >> 16) & 0xff);
        if(x == 8388638){
            String xz = "";
            byte[]array = outputStream.toByteArray();
            System.out.println(array[2]+" | " + (char)array[2]);
        }
    } else {
        writer.writeInt(x);
    }
}

this works fine but when I do dumpInt(8388638), I get some weird occurrences. writer.writeByte((x >> 16) & 0xfff) writes -128 to the writer Im clueless as why that is.

The same thing happens for outputStream.write((x >> 16) & 0xff); but when I run System.out.println((x >> 16) & 0xff); it outputs 128(positive)

Does anybody know why this is and how I can correct it? (Sorry i'm somewhat experienced in java but im not the best by any means so if this is a really simple fix my bad)

Upvotes: 3

Views: 147

Answers (3)

WJS
WJS

Reputation: 40034

The same thing happens for outputStream.write((x >> 16) & 0xff); but when I run System.out.println((x >> 16) & 0xff); it outputs 128(positive) Does anybody know why this is and how I can correct it?

int x = 8388638;

When you do the following:

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write((x >> 16) & 0xff); 
System.out.println(outputStream.toByteArray()[0]);

You are printing a -128 because the value is 0b1000_0000 which is -128 in two's complement form for a byte (the high order bit being the sign bit).

When you do this:

System.out.println((x >> 16) & 0xff);

(x>>16) is still an int and then the & masks off the top 24 bits. So the low order 8 bits print as an int and two's complement does not come into play.

For more information check out Two's Complement

Upvotes: 1

Sweeper
Sweeper

Reputation: 271105

Bytes in Java are signed, and have the range -128~127. You seem to be more familiar with the other decimal representation of a byte - the unsigned byte, having a range from 0 to 255. However, that doesn't exist in Java as a separate data type.

The following shows corresponding values between the two representations:

  signed    -128 -127 -126 ... -1   0   1   ...   126 127 128
unsigned    128  129  130  ... 255  0   1   ...   126 127 128

Note: these are not really different representations in binary. They have the same binary representation. It depends on whether you interpret the MSB as the sign bit or not.

You can convert from the signed version to the unsigned version by Byte.toUnsignedInt, and you can convert from the unsigned version to the signed version by casting to byte.

For some reason that you'd have to ask the API designers, OutputStream.write accepts unsigned bytes (as well as signed ones). How does Java do this if byte only ranges from -128 to 127? It accepts an int rather than a byte! This is the reason why your code compiles at all.

write (which writeByte also calls) converts the int 128 to a (signed) byte -128 and writes it into the stream. When you get the written bytes later, you are getting the converted signed byte. But in fact, in terms of the bits, 128 and -128 are the same 1000 0000. It's just that Java insists on interpreting the first 1 as a sign bit.

As I said before, you can convert the -128 to 128` if you prefer the other representation:

Byte.toUnsignedInt(-128) // 128

Note that the result must be an int, because the result is outside of byte's range.

Upvotes: 1

rzwitserloot
rzwitserloot

Reputation: 102872

In java, a byte has the range of [-128, +127], as in, +128 is literally impossible. In fact, -128 is how a byte that holds +128 is printed (in the sense that +128 is, in bits, 1000 0000, and as a java byte, as they are signed, that is rendered as -128, because that's also 1000 0000.

In other words, your app is working fine.

Your linked picture in your comment is due to you misunderstanding how parentheses and operator priority works in java.

System.out.println((byte)(x>>16)&0xff);

resolves like so:

(byte)(x>>16) & 0xFF

and as 0xFF is an int (all numeric literals are always an int, unless they end in a capital L, then they are a long. You can't put a byte or short literal in java code), that's an operation between a byte and an int, and like all ops between bytes and ints, this is resolved by first upgrading the byte to an int, and then returning the result by doing the op on the two resulting ints. In other words, it is this:

(int)((byte)(x>>16) & 0xFF

And that'd be +128.

Try this:

(byte)((x>>16) & 0xFF). You'll find it prints -128 as expected.

Upvotes: 1

Related Questions