18

I have a bunch of hex strings, one of them, for example is:

  d1bc4f7154ac9edb

which is the hex value of "-3333702275990511909". This is the same hex you get if you do Long.toHexString("d1bc4f7154ac9edb");

For now, let's just assume I only have access to the hex string values and that is it. Doing this:

  Long.parseLong(hexstring, 16);

Doesn't work because it converts it to a different value that is too large for a Long. Is there away to convert these unsigned hex values into signed longs?

Thanks!

Peter
  • 837
  • 3
  • 14
  • 19

4 Answers4

22

You can use BigInteger to parse it and get back a long:

long value = new BigInteger("d1bc4f7154ac9edb", 16).longValue();
System.out.println(value); // this outputs -3333702275990511909
WhiteFang34
  • 70,765
  • 18
  • 106
  • 111
  • 6
    Just a note for modern visitors: as Scott Carey remarks, starting from Java 8 we can simply do `Long.parseUnsignedLong(hexstring, 16)`, so cool :) – NIA Sep 05 '17 at 14:58
6

The prior answers are overly complex or out of date.

Long.parseUnsignedLong(hexstring, 16)

Scott Carey
  • 1,587
  • 17
  • 13
6

You may split it in half and read 32 bits at a time. Then use shift-left by 32 and a logical or to get it back into a single long.

Knut Forkalsrud
  • 1,134
  • 1
  • 7
  • 19
  • 2
    Something roughly like: (Long.parseLong(hexstring.substring(0, 8), 16) << 32) | (Long.parseLong(hexstring.substring(8, 16), 16) << 0) – Knut Forkalsrud Jun 10 '11 at 02:01
4

The method below has the benefit of not creating another BigInteger object every time you need to do this.

public class Test {
  /**
   * Returns a {@code long} containing the least-significant 64 bits of the unsigned hexadecimal input.
   * 
   * @param  valueInUnsignedHex     a {@link String} containing the value in unsigned hexadecimal notation
   * @return                        a {@code long} containing the least-significant 64 bits of the value
   * @throws NumberFormatException  if the input {@link String} is empty or contains any nonhexadecimal characters
   */
  public static final long fromUnsignedHex(final String valueInUnsignedHex) {
    long value = 0;

    final int hexLength = valueInUnsignedHex.length();
    if (hexLength == 0) throw new NumberFormatException("For input string: \"\"");
    for (int i = Math.max(0, hexLength - 16); i < hexLength; i++) {
      final char ch = valueInUnsignedHex.charAt(i);

      if      (ch >= '0' && ch <= '9') value = (value << 4) | (ch - '0'         );
      else if (ch >= 'A' && ch <= 'F') value = (value << 4) | (ch - ('A' - 0xaL));
      else if (ch >= 'a' && ch <= 'f') value = (value << 4) | (ch - ('a' - 0xaL));
      else                             throw new NumberFormatException("For input string: \"" + valueInUnsignedHex + "\"");
    }

    return value;
  }

  public static void main(String[] args) {
    System.out.println(fromUnsignedHex("d1bc4f7154ac9edb"));
  }
}

This produces

-3333702275990511909
Olathe
  • 1,885
  • 1
  • 15
  • 23
  • This looks good, but instead of obtaining the digit values as you did above, maybe it would be best to use Character.digit? http://docs.oracle.com/javase/7/docs/api/java/lang/Character.html#digit(int,%20int) – asieira Jul 23 '14 at 15:00
  • `Character.digit` includes all Unicode decimal digits, and I don't want to handle all of them since there are several sets of them, some of which aren't properly documented. – Olathe Jul 27 '14 at 21:15
  • I believe that being able to extract the numeric value of any of the possible Unicode digits is a plus by making your code more portable and internatinoalization-friendly. Even if your project does not need it, maybe stack overflow users would benefit if you updated your answer. Just my 2 cents. – asieira Jul 28 '14 at 22:45