rjcarr
rjcarr

Reputation: 2091

Understanding signed numbers and complements in java

I have a 3 byte signed number that I need to determine the value of in Java. I believe it is signed with one's complement but I'm not 100% sure (I haven't studied this stuff in more than 10 years and the documentation of my problem isn't super clear). I think the problem I'm having is Java does everything in two's complement. I have a specific example to show:

What I think is happening is I'm getting the two's complement of the entire int and not just the 3 bytes of the int, but I don't know how to fix this.

What I have been able to do is convert the integer to a binary string (Integer.toBinaryString()) and then manually "flip" all of the 0s to 1s and vice-versa. When then parsing this integer (Integer.parseInt(s, 16)) I get 1172712 which is very close. In all of the other examples I need to always add 1 to the result to get the answer.

Can anyone diagnose what type of signed number encoding is being used here and if there is a solution other than manually flipping every character of a string? I feel like there must be a much more elegant way to do this.

EDIT: All of the responders have helped in different ways, but my general question was how to flip a 3-byte number and @louis-wasserman answered this and answered first so I'm marking him as the solution. Thanks to everyone for the help!

Upvotes: 1

Views: 717

Answers (3)

Petr
Petr

Reputation: 63419

Negative signed numbers are defined so that a + (-a) = 0. So it means that all bits are flipped and then 1 added. See Two's complement. You can check that the condition is satisfied by this process by thinking what happens when you add a + ~a + 1.

You can recognize that a number is negative by its most significant bit. So if you need to convert a signed 3-byte number into a 4-byte number, you can do it by checking the bit and if it's set, set also the bits of the fourth byte:

if ((a & 0x800000) != 0)
    a = a | 0xff000000;

You can do it also in a single expression, which will most likely perform better, because there is no branching in the computation (branching doesn't play well with pipelining in current CPUs):

a = (0xfffffe << a) >> a;

Here << and >> perform byte shifts. First we shift the number 8 bits to the right (so now it occupies the 3 "upper" bytes instead of the 3 "lower" ones), and then shift it back. The trick is that >> is so-called Arithmetic shift also known as signed shift. copies the most significant bit to all bits that are made vacant by the operation. This is exactly to keep the sign of the number. Indeed:

(0x1ffffe << 8) >> 8        ->  2097150
(0xfffffe << 8) >> 8        ->  -2

Just note that java also has a unsigned right shift operator >>>. For more information, see Java Tutorial: Bitwise and Bit Shift Operators.

Upvotes: 0

MrSmith42
MrSmith42

Reputation: 10171

0xFFEE1B17 is -1172713 You must only add the leading byte. FF if the highest bit of the 3-byte value is set and 00 otherwise.

A method which converts your 3-byte value to a proper intcould look like this:

if(byte3val>7FFFFF)
  return byte3val| 0xFF000000;
else 
  return byte3val;

Upvotes: 1

Louis Wasserman
Louis Wasserman

Reputation: 198481

If you want to flip the low three bytes of a Java int, then you just do ^ 0x00FFFFFF.

Upvotes: 2

Related Questions