1

My goal is to understand the two's complement.

10000111 in signed integer equals to -121. However, if you bitwise AND it with 0xFF in hexadecimal or 11111111 in decimal, it will equals to 135.

In my view, this should not happen because the top byte 10000111 is identical to the result byte 10000111

    10000111
AND 11111111
------------
    10000111 = 135.

My expected result is the value should not change, which is equals to -121.

My actual result is the value changes.

My guess is that, the 0xFF is a unsigned bit. Therefore, the signed bit 1 on the top byte and unsigned bit 1 on the lower byte will result in unsigned bit 1. But... that could not be right.

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class WebSocketWorkerThreadTest {
  @Test
  void SignedToUnsignedInteger() {
    byte signed = (byte) 0b10000111;
    // If java uses unsigned integer, it should be 135. however, java uses signed integer. Therefore, the outputs will be -121
    // 10000111
    // ^
    // the sign bit. If it exists then the value will be negative.
    Assertions.assertEquals(-121, signed);
    // 10000111
    // 11111111
    // --------
    // 10000111
    int unsigned = signed & 0xFF;
    Assertions.assertEquals(135, unsigned);
  }
}
Jason Rich Darmawan
  • 1,607
  • 3
  • 14
  • 31
  • 1
    `signed` is promoted to `int` when you apply the `&` operator. It's not `10000111 & 11111111`, it's `11111111111111111111111110000111 & 00000000000000000000000011111111`. – Andy Turner Jul 20 '21 at 05:25
  • @AndyTurner ahh I see. In other words, before I apply the `&` operator, the byte signed value is `10000111` in binary. However, right before the operation, it gets converted to `32-bit` and then it's get AND-ing by the lower byte of `00000000000000000000000011111111`? – Jason Rich Darmawan Jul 20 '21 at 05:30
  • Java does not support signed vs unsigned for it's primitive types, as @user16320675 states. All values are signed, except for characters, which are always positive. – TreffnonX Jul 20 '21 at 05:32

2 Answers2

2

signed is promoted to int when you apply the & operator, because of binary numeric promotion.

It's not 10000111 & 11111111, it's 11111111111111111111111110000111 & 00000000000000000000000011111111, the value of which is 00000000000000000000000010000111 (still an int).

The MSB here is zero, hence it's positive.

If you cast it back to a byte, which would take just the 8 LSBs, that byte would be negative again.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • this is correct, but fixable. OP should cast to byte. – TreffnonX Jul 20 '21 at 05:28
  • @TreffnonX binary numeric promotion would cast it straight back to int. And `(int) (byte) 0xFF = -1`, so you'd be doing `(int) aByte & -1`, which is simply `(int) aByte`, so all you'd achieve by doing that would be doing is widening a byte to int. – Andy Turner Jul 20 '21 at 05:32
  • `byte unsigned = (byte) (signed & 0xFF);` works perfectly well. It returns `-121` as expected – TreffnonX Jul 20 '21 at 05:39
  • @TreffnonX Why cast back to a byte, then you're just back where you started. – Andy Turner Jul 20 '21 at 05:39
  • Exactly. That is what OP expected. OP stated: "My expected result is the value should not change, which is equals to -121.". OP wants to apply a non-op, when applying an all-1-bit-value. Therefore he should treat all upper bits as redundant and just remove them after the op by casting to byte. – TreffnonX Jul 20 '21 at 05:40
  • @AndyTurner does widening a byte to int will give a performance penalty? – Jason Rich Darmawan Jul 20 '21 at 05:41
  • @kidfrom vs what? The widening is unavoidable in Java. – Andy Turner Jul 20 '21 at 05:45
  • @AndyTurner vs int. I am referencing [this](https://stackoverflow.com/questions/14531235/in-java-is-it-more-efficient-to-use-byte-or-short-instead-of-int-and-float-inst). what do you think? – Jason Rich Darmawan Jul 20 '21 at 05:52
  • @kidfrom doing an operation can't be more performant than not. The operation can be avoided by using a wider data type, but that can't use less memory than a narrower data type. It's a design choice you have to make; but if by "performance penalty" you mean the speed difference, it's essentially zero. – Andy Turner Jul 20 '21 at 08:42
0

Java stores integers (, long and byte) in the two's complement. See https://en.wikipedia.org/wiki/Two%27s_complement

In this representation, any negative number begins with a bunch of 1-bits, because the value 10000... 0000 is the lowest representable value and the value 1111... 111 is -1. For that reason, when you clip the leading 1-bits, you completely shift the number up into the positive range, because the new number does not start with a 1-bit anymore.

In your case, you use byte, but the value of your byte does not have the form you expect. It is an integer, when the & operation is applied, because you did not cast 0xFF to byte. The easiest to fix this, is to cast the result of the & operation to byte, as suggested by another answer.

TreffnonX
  • 2,924
  • 15
  • 23