问题
Im trying to implement a class that stores a 32-bit number without using the int
primitive type. For doing so, I'm using two short
variables msbs
and lsbs
to store the 32 bits of the number, 16 bits in each variable.
The variable msbs
will store the first 16 bits of the number and the lsbs
variable the 16 bits left.
When It comes to save the given bytes to the variables I apply the next formula: (The bytes order are given as Little-Endian notation)
Input -> byte[] n = {0b00110101, -3, 0b1001, 0b0};
to the number 0b00000000 00001001 11111101 00110101 (654645)
msbs = ((n[3] << 8) | n[2]);
lsbs = ((n[1] << 8) | n[0]);
As shown below
private void saveNumber(byte[] n) {
msbs = (byte)((n[3] << 8) | n[2]);
lsbs = (byte)((n[1] << 8) | n[0]);
System.out.println(Integer.toBinaryString((n[1] << 8) | n[0]));//Prints 11111111111111111111110100110101
System.out.println("msbs -> " + Integer.toBinaryString(msbs));
System.out.println("lsbs -> " + Integer.toBinaryString(lsbs));//Prints 110101
}
The line
System.out.println(Integer.toBinaryString((n[1] << 8) | n[0]));//Prints 11111111111111111111110100110101
prints exactly what I need, despite the huge amount of useless 1's bits at the beggining (of which I can get rid of just by casting it to short
)
But when I print the lsbs
where I store the exact same value (apparently) it outputs 110101 when It should be 0b1111110100110101
Why does this behavior occur? I understand It must be something with the "internal" casting performed by Java at the time of store the value 11111111111111111111110100110101
into a 16 bits primitive type (Which personally, I think sholdn't be happening because I am shifting 8 bits to the left in an 8-bits number which should give me a 16-bits number)
As a side note, the msbs
variable is doing exactly what I want it to do, so the problem should be related to the way that Java represents the negative numbers
Btw, I know Java isn't exactly the best language to have fun with bits.
回答1:
Why does this behavior occur?
In Java, all bitwise operations are 32 or 64 bit operations. This is different from some other languages, and can be unexpected. But it is what it is.
I understand It must be something with the "internal" casting performed by Java ....
Java doesn't do an implicit narrowing casts in any of your examples1. In fact, I think that the cause of the unexpected behaviour is an explicit narrowing cast in your code:
msbs = (byte)((n[3] << 8) | n[2]);
You have explicitly cast a 32 bit value from ((n[3] << 8) | n[2])
to a byte
. Based on what you say you expect, you should be casting to short
.
Aside: When you write things like this "Which personally, I think sholdn't be happening ..." it implies that you are doubting the correctness of the Java compiler. In fact, in 99.999% of cases2, the real problem is someone does not understand what the compiler should do with their; i.e. their knowledge of the language is too shallow3. In most cases, there is a specification of the programming language that says precisely what a particular construct means. In the Java case, it is the Java Language Specification.
1 - In fact the only cases I can think of where internal narrowing happens with primitive types are in the assignment operators.
2 - I made that number up, but the point is that compiler bugs are rarely the cause of unexpected application behaviour.
3 - Or maybe it is just an application bug that the programmer has missed. Tiredness can do bad things to the brain ...
来源:https://stackoverflow.com/questions/32161747/bit-manipulation-in-negative-byte-and-short-data-types-in-java