7

I am reading 8 bytes of data in from a hardware device. I need to convert them into a numeric value. I think I want to convert them to a long as that should fit 8 bytes. I am not very familiar with Java and low level data type operations. I seem to have two problems (apart from the fact there is almost no documentation for the hardware in question), The bytes are expecting to be unsigned, so I can't do a straight integer conversion. I am not sure what endianness they are.

Any advice would be appreciated.


Ended up with this (taken from some source code I probably should have read a week ago):

public static final long toLong (byte[] byteArray, int offset, int len)
{
   long val = 0;
   len = Math.min(len, 8);
   for (int i = (len - 1); i >= 0; i--)
   {
      val <<= 8;
      val |= (byteArray [offset + i] & 0x00FF);
   }
   return val;
}
LarsH
  • 27,481
  • 8
  • 94
  • 152
Jotham
  • 1,122
  • 1
  • 10
  • 23
  • 1
    Why do you need to convert those 8 bytes into a single numeric value? If they are indeed component bytes of a larger value, then they are neither "signed" nor "unsigned". If you don't know what their endianness is, then you simply can't interpret them as some long value. – Jonathan Feinberg Oct 19 '09 at 04:00
  • I know they are successive values (a magnetic counter) so through trial and error I can find when it reports the correct incrementation. I need to convert them to a single numeric value to determine its rate of change. – Jotham Oct 19 '09 at 04:03
  • You should replace 0x00FF by 0xFFL as that would better express your intent of converting a byte to a long. – starblue Oct 19 '09 at 06:32
  • When you say "Ended up with this" .. it doesn't seem to me to have results that agree with erickson's answer, which seem correct. – dfrankow Apr 15 '10 at 15:21

11 Answers11

16

Shifting bytes according to the endianness of the data is fairly straightforward. There is a small trick with the long datatype, however, because a binary operation on integral types of int or smaller promotes the operands to int. For left shifts larger than 31 bits, this will result in zero, since all of the bits have been shifted out of the int range.

So, force promotion to long by including a long operand in the calculation. Below, I do this by masking each byte with the value 0xFFL, which is a long, forcing the result to be a long.

byte[] buf = new byte[8];
/* Fill buf somehow... */
long l = ((buf[0] & 0xFFL) << 56) |
         ((buf[1] & 0xFFL) << 48) |
         ((buf[2] & 0xFFL) << 40) |
         ((buf[3] & 0xFFL) << 32) |
         ((buf[4] & 0xFFL) << 24) |
         ((buf[5] & 0xFFL) << 16) |
         ((buf[6] & 0xFFL) <<  8) |
         ((buf[7] & 0xFFL) <<  0) ;
erickson
  • 265,237
  • 58
  • 395
  • 493
8

Byte#longValue() should do it

And if not (thanks for the source example) you can use java.nio.ByteBuffer such as in

 public static long toLong(byte[] b) {
    ByteBuffer bb = ByteBuffer.allocate(b.length);
    bb.put(b);
    return bb.getLong();
}

The initial order is BIG_ENDIAN you can reed more here

Bostone
  • 36,858
  • 39
  • 167
  • 227
  • I had something like that but it was still no good. Maybe it's because I am new to Java though. public static long bufferToLong (byte[] b) { long value = b[0]; for (int i=1;i – Jotham Oct 19 '09 at 04:06
  • So what's the -1 for? Anyways - I extended the answer to give you maybe another idea. From what I know - java.nio is pretty fast (faster than your regular variety streams) – Bostone Oct 19 '09 at 15:45
  • 2
    I get a `BufferUnderflowException` unless I call `bb.flip();` before I call `getLong()` – das_j Aug 04 '15 at 09:04
6
ByteBuffer buffer = ByteBuffer.wrap(bytes);
buffer.getLong();
Cork Kochi
  • 1,783
  • 6
  • 29
  • 45
5

I believe that you could benefit from using java.nio. This is how you can store 8 bytes in a long:

// Byte Array TO Long
public static long batol(byte[] buff) {
    return batol(buff, false);
}

public static long batol(byte[] buff, boolean littleEndian) {
    assert(buff.length == 8);
    ByteBuffer bb = ByteBuffer.wrap(buff);
    if (littleEndian) bb.order(ByteOrder.LITTLE_ENDIAN);
    return bb.getLong();
}

Of course, the resulting longs will have signed representation, but they will have identical binary values to the source data. For a 64 bit+ unsigned representation and arithmatic, you'll need to use BigInteger. Here's how to convert from "unsigned" data stored in a long to a correct BigInteger:

// "Unsigned" Long TO Big Integer
public static BigInteger ultobi(long ul) {
    byte[] buff = new byte[8];
    ByteBuffer.wrap(buff).asLongBuffer().put(ul);
    return new BigInteger(+1, buff);
}
Gunslinger47
  • 7,001
  • 2
  • 21
  • 29
2

For the endianness, test with some numbers you know, and then you will be using a byte shifting to move them into the long.

You may find this to be a starting point. http://www.janeg.ca/scjp/oper/shift.html

The difficulty is that depending on the endianess will change how you do it, but you will shift by 24, 16, 8 then add the last one, basically, if doing 32 bits, but you are going longer, so just do extra shifting.

James Black
  • 41,583
  • 10
  • 86
  • 166
  • With extra shifting, you have to watch out that you are shifting a `long`, not an `int`, because shifting more than 24 bits in an `int` will discard the highest order bits. – erickson Oct 19 '09 at 04:24
  • He stated it was a long, so I figure he can determine how many bits it should be that he really needs. – James Black Oct 19 '09 at 04:28
1

Take a look at BigInteger(byte[]). It is almost what you want except that it is a signed one. So you may add one more byte to it before you pass it on to BigInteger.

Another thing is that you should be aware of what endian your bytes are.

Hope this helps.

NawaMan
  • 25,129
  • 10
  • 51
  • 77
0

If you're reading from an InputStream, you may also want to look at DataInputStream.readLong(). Java 1.5 introduced Long.reverseBytes(long) which may help you with endianness.

McDowell
  • 107,573
  • 31
  • 204
  • 267
0

You can use:

byte bVal = 127;
Long longVar = Long.valueOf(bVal);
Stephen Rauch
  • 47,830
  • 31
  • 106
  • 135
0
 public static long convertToLong(byte[] array) 
 {
   ByteBuffer buffer = ByteBuffer.wrap(array);
   return buffer.getLong();
 }
Vikki
  • 1,897
  • 1
  • 17
  • 24
0

The next code parses bytes as a signed number of arbitrary length ≤ 8.

static long bytesToSignedNumber(boolean reverseOrder, byte... bytes) {
    if (bytes.length > 8) bytes = Arrays.copyOfRange(bytes, 0, 8); //delete this line to ignore the higher excess bytes instead of the lower ones

    int l = bytes.length;
    long number = 0;
    for (int i = 0; i < l; i++) {
        long current;
        if (l==3 || l==5 || l==6 || l==7 //delete this line if you want to operate with 3,5,6,7-byte signed numbers (unlikely)
                || !reverseOrder && (i > 0)
                || reverseOrder && (i < l-1)) {
            current = ((long) bytes[i]) & 0x0000_0000_0000_00ff; //unsigned
        } else {
            current = (long) bytes[i];                           //signed
        }
        if (reverseOrder) number += current << (8 * i);
        else number = (number << 8) + current;
    }

    return number;
}

It parses an byte array as a number, of minimum existing type, converted to long. A few examples:

bytesToSignedNumber(false, 0x01, 0x04) returns 260 (2 bytes as short)

bytesToSignedNumber(false, 0xF1, 0x04) returns -3836 (2 bytes as short)

bytesToSignedNumber(false, 0x01, 0x01, 0x04) returns 65796 (3 bytes as int)

bytesToSignedNumber(false, 0xF1, 0x01, 0x04) returns 15794436 (3 bytes as int)

bytesToSignedNumber(false, 0xF1, 0x01, 0x01, 0x04) returns -251592444 (4 bytes as int)

bytesToSignedNumber(false, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x04) returns 1081146489369067777 (8 of 9 bytes as long)

antaki93
  • 704
  • 7
  • 10
0

Another Alternative

From Google

com.google.common.primitives

  Longs.fromByteArray(bytes);
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
KUTAY ZORLU
  • 93
  • 1
  • 9