1

I have a 14 bit, two's complement value being sent to my java application form a bluetooth device in the form of a byte array.

I am unable to change the format of the data sent from the bluetooth device.

How do I get this value into an Integer or Short and maintain the sign?

A few things I have tried are

Short x = ByteBuffer.wrap(value).getShort();

Integer x = (short)(value[3] & 0xff) | (short)((value[2] << 8) & 0xff00);

The problem I think I encounter with the above code is when I receive a negative number, for example

-8192 (in two's complement 10 0000 0000 0000)

Because the receiving data type is 16 bits it expects the bit that negates the value to be 16 along but seen as what it actually receives is

0010 0000 0000 0000

It considers this 8192 and not -8192.

rogermushroom
  • 5,486
  • 4
  • 42
  • 68
  • In your binary source are you only supposed to read 14 bits or is your 14 bit signed integer being stored as a Short? When a number goes negative, all bit positions are set to 1 and as the number becomes larger negative the 1s are replaced with 0s (basically the exact opposite of what happens when you count up in binary) – bond Oct 24 '14 at 14:20
  • It is received as a byte array but I guess I should only read the 14 bits. – rogermushroom Oct 24 '14 at 14:27

4 Answers4

3

Simply extract the number from the byte array, ignoring signedness and excess bits completely:

byte[] data = ...
int rawNum = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);

Now left shift the raw value by (type size - number of valid bits) then right shift using the signed shift operator

int realNum = (rawNum << (32 - 14)) >> (32 - 14);

This will first move the sign bit into the sign bit of the type storing the value (here: int) by shifting left, then when right shifting the sign bit is replicated shift-distance times (as defined by the shift operator).

Durandal
  • 19,919
  • 4
  • 36
  • 70
2

You should extend the most significant bit to the left.

Offhand: short y = (short)((x & 0b10000000000000 ) > 0 ? ( x | 0b1100000000000000 ) : x);

That is, if the most significant bit (for a 14 bit number) is 1, then the result of the & is going to be non-zero. If so, add the two additional ones to the left with an or. Otherwise leave as is.

RealSkeptic
  • 33,993
  • 7
  • 53
  • 79
  • I haven't seen this way of declaring bytes before (0b1000000000000000) is it supposed to work or is it just short hand notation? When I try it in my editor it registers a compilation error. Also if I'm receiving the data as a byte array I guess I just use something like this to convert to `int` - `Integer rawX = (short)(value[3] & 0xff) | (short)((value[2] << 8) & 0xff00);` – rogermushroom Oct 24 '14 at 14:49
  • 1
    I tested this before posting, using Eclipse Luna and JDK 7. Your Java version may be lower than 7. See [documentation](http://docs.oracle.com/javase/8/docs/technotes/guides/language/binary-literals.html). – RealSkeptic Oct 24 '14 at 14:52
  • Note that Java 6 has not been publicly updated since Februari 2013 – Maarten Bodewes Oct 24 '14 at 14:55
  • Thanks for the heads up on byte literals. I'm actually using Java 7 in Android Studio and for some reason it is still showing up as an error but that is another issue. – rogermushroom Oct 24 '14 at 15:01
  • You can also use underscores in all numbers e.g. 1_500_000 – bond Oct 24 '14 at 15:02
  • http://stackoverflow.com/questions/17637179/how-to-set-source-1-7-in-android-studio-and-gradle might be of interest for Android, but we're diverting from the original question indeed. – Maarten Bodewes Oct 24 '14 at 15:06
2

I've generated a small example that uses integers, It should be easy to put the short you've retrieved in the integer. I've shown an example using your value variable just to be sure.

/**
 * Expects the value to contain a 2-complement number stored in the
 * least significant bits.
 * The contents of the most significant bits is ignored.
 * @param value the value to convert
 * @param bitSize the size of the 2 complement number in the value
 * @return the converted value
 */
public static int fromTwoComplement(int value, int bitSize) {
    int shift = Integer.SIZE - bitSize;
    // shift sign into position
    int result  = value << shift;
    // Java right shift uses sign extension, but only works on integers or longs
    result = result >> shift;
    return result;
}

public static void main(String[] args) {
    System.out.println(fromTwoComplement(0b00_00_0000_0000_0001, 14));
    System.out.println(fromTwoComplement(0b00_01_1111_1111_1111, 14));
    System.out.println(fromTwoComplement(0b00_10_0000_0000_0000, 14));
    System.out.println(fromTwoComplement(0b00_11_1111_1111_1111, 14));

    // largest negative
    byte[] value = new byte[] { 0b0010_0000,  0b0000_0000 };
    System.out.println(fromTwoComplement((int) ByteBuffer.wrap(value).getShort(), 14));

}
Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • My apologies for the many updates, I used 5 bits in one nibble during testing, and my results were - as you expect - quite erratic. – Maarten Bodewes Oct 24 '14 at 14:57
1
public static void main(String[] args)
{
            System.out.println((short) 0x1FFF);
    // 0001 1111 1111 1111 - maximum positive value

    System.out.println((short) 0x3FFF);
    // 0011 1111 1111 1111 - minimum negative value

    System.out.println((short) 0xF);
    // 0000 0000 0000 1111 - positive 15

    System.out.println((short) 0x3FF1);

    // 0011 1111 1111 0001 - negative 15

    System.out.println(convert14bit((short) 0x3FFF)); // returns -1

    System.out.println(convert14bit((short) 0x3FF1)); // returns -15

}

public static final short convert14bit(short value)
{
    if ((value & 0x2000) > 0)
    {
        // negative number

        return (short) (value | 0xE000);
    }
    else
    {
        return value;
    }
}
bond
  • 1,054
  • 8
  • 15