views:

126

answers:

2

Say I read these bytes: "6F D4 06 40" from an input device. The number is a longitude reading in MilliArcSeconds format. The top bit (0x80000000) is basically always zero and is ignored for this question.

I can easily convert the bytes to an unsigned integer: 1876166208

But how do I convert that unsigned value into its final form of 31-bit signed-integer?

So far all I've come up with is:

  1. if value & 0x40000000 then it's actually negative, need to convert it
  2. If it's negative, strip the top bit and do something with the remaining bits...

So I can tell if it's a negative number, but in order to know what value the negative number is, I have to do something with the remaining bits - a one's compliment? How do I do that in Java?

Another way to put the question is, how do I convert an unsigned integer into a 31-bit signed integer in Java?

Thank you!

+2  A: 

The answer depends on what the lower 31 bits of your input are meant to represent.

int input = 0x6FD40640 & 0x7FFFFFFF; //strip top bit; only here for clarity

Unsigned input: 0x6FD40640 == 1876166208

Two's complement (desired output: -271317440)

A two's complement integer is one where -1 has all bits set, and lower negatives number count down from there. The first bit still acts as a sign bit.

1000 -> -8
1001 -> -7
...
1110 -> -2
1111 -> -1
0000 ->  0
0001 ->  1

If the lower 31 bits represent a two's complement integer, then I think you should just be able to do this:

input = (input << 1) >> 1;

That's because Java stores integers in two's complement internally: all we do is shift left and then shift back right (signed) so that the sign bit is picked up and the integer goes from 31 bits to 32 bits.

One's complement (desired output: -802424384)

A one's complement number representation is one where the first bit is a dedicated sign bit, and the remaining bits represent the magnitude. The lower bits of -100 will be the same as the lower bits of 100:

 1111 -> -7
 1110 -> -6
 ...
 1001 -> -1
 1000 -> -0 (anomoly)
 0000 ->  0
 0001 ->  1

If the lower 31 bits represent a one's complement integer (that is, a sign bit followed by 30 bits representing an unsigned magnitude), then you need to convert it into two's complement so that Java extracts the value properly. To do this you just need to extract the lower 30 bits and multiply by -1:

if ( input & 0x40000000 ) {
   input = (input & 0x3FFFFFFF) * -1;
}

You said in the question's comments that after converting to degrees (dividing by 3600000) you get around -75.36. When I divide -271317440 by 3600000 I get -75.36595555555556, so I'm guessing your input format is two's complement, so my first and original answer was correct.

Mark Peters
Alison
Brad Hein
@Alison: no that was just there to demonstrate that the input was only the lower 31 bits, and that the high bit is irrelevant. The second line is what I was trying to show, but Brad hasn't confirmed whether that's the desired output or not.
Mark Peters
Mark Peters
Why is this answer getting up-voted? It doesn't address the question at all. Please re-read the question!
Brad Hein
Accepted. Great work!
Brad Hein
A: 

Reading an unsigned integer as signed is a matter of identifying whether the most significant bit (negative flag) is set, and if so, flip all bits of the number (thus clearing the most significant bit, and switching the number to its negative representation. When performing the aforementioned process you must also make note of the fact that the resultant number is a negative.

// Convert the hex bytes to an unsigned integer
long MAS = Integer.ValueOf("6F D4 06 40".replace (" ",""),16);
boolean isLongitudeNegative = false;

// Is the negative-bit set? If so, strip it and toggle all bits.
if (MAS & 0x40000000 > 0) {
    // then it's negative, so strip the negative bit and flip all the other bits
    MAS ^= 0xFFFFFFFF;
    // Throw away the unused bit.
    MAS &= 0x7FFFFFFF;
    isLongitudeNegative = true;
}

// Now convert from MAS to degrees minutes seconds
String DMS = convertMASToDMS(isLongitudeNegative,MAS);
Brad Hein
Sorry for the outbursts. Did this answer end up doing the trick for your requirements? If so feel free to accept it. Only suggestion is that you might find `MAS = ~MAS` to be a more clear way of toggling the bits of MAS. So given this solution, I assume the lower 31 bits of the input represent a 31-bit one's complement integer, and you're trying to extract that value? Is that the problem you were trying to solve?
Mark Peters
@Brad: Check my answer again. I'm pretty sure my original answer was correct. When I apply my transformation to your sample input and then divide by 3600000 I get -75.369555 which is your expected output. When I do your transformation I get -222 which doesn't jive.
Mark Peters