I know that >>> is unsigned shift-right, and since byte in Java is signed 8-bit then -1 will be represented as 1111 1111. As a result, for the code below:
byte b = (byte) -1;
b >>>= 1;
System.out.println(b);
Shouldn't it print 127 (0111 1111 in binary)? But the result I get is always -1, no matter how many times I perform >>> to b (e.g. in a loop).
Can anyone explain why and in what way I can get the result I expect (i.e. to perform unsigned right shift in which -1 shifted right once equals 127 for a byte in Java)? Is my understanding wrong? Thanks a lot!
You're running into a chain of really weird java design choices that make this code 'lie' (it appears to do one thing but it actually won't do that thing). Let's break it down:
Lesser known java aspect 1:
byte,short,char, andbooleanare inferiorIn java,
byte,short,char,booleanare the inferior primitives, withint,long,doubleandfloatbeing the superior primitives. There are no opcodes for the inferiors beyond array store and array load. There is no opcode in java that adds 2 bytes together. It just does not exist.. It is literally impossible to do this, given thatjavachas no idea what to generate. So, instead:Is actually taken to mean:
And, thus, this is a compiler error - the result of
int + intisint, and trying to assign anintto abyteis not allowed without explicit casting.You can trivially have a look at any list of java bytecodes. You'll find FADD, DADD, IADD, and LADD (respectively, 'float add', 'double add', 'int add' and 'long add'). But you won't find byte/short/char variants; there is no BADD, CADD, or SADD.
Lesser known java aspect 2: The compound assignment operators auto-cast.
this:
a += b;is not actually identical toa = a + b;at all! I know that's what you were taught, but this is incorrect. It is, in fact, equal toa = (byte) (a + b);where thatbyteis the type ofaitself:versus:
Totally legal. And results in x being 10. The full flow is:
doublebecause java can only do math between 2 things of the same type.dis 5.5, already a double, nice.DADDbytecode is executed; the result of this is 10.5.int, resulting in10, which is assigned tox.Run
javapif you want to see this.Combining the two
Thus, let's break down your code:
This makes the int -1 (32 '1' bits), then casts it to byte, resulting in
bhaving the value1111 1111. So far, so good, nothing surprising here.Hoo boy. This looks simple but is very complicated. It's syntax sugar for:
To break that down:
(int) bresults in 32 1-bits.bis -1 in byte, casting that to int produces -1 the int, which is 32 1-bits.>>> 1then makes that a zero, followed by 31 1 bits.So how do I do this?
Don't use
byte, basically. Don't ever use the inferior primitives except under the following circumstances:byte[]is useful and really does only take 1 byte per item.For all other things (such as single fields, and local vars aimed at math, such as what you are doing), don't. They don't act like you think they act, as you have found out here.
Just go with ints. If you want an 'unsigned byte', just make an
intvariable and use that:byteas a single value (notbyte[]- justbyte) is not more efficient in any way thanintor likely evenlong, on 64-bit architecture. Java does not assign less memory (because all things have to align on word boundaries), the operation is not faster (CPUs work in 64-bit chunks already).