3

When I do

Long.parseUnsignedLong("FBD626CC4961A4FC", 16)

I get back -300009666327239428

Which seems wrong, since the meaning of unsigned long according to this answer https://stackoverflow.com/a/2550367/1754020 is that the range is always positive.

To get the correct number from this HEX value I do

BigInteger value = new BigInteger("FBD626CC4961A4FC", 16);

When I print value it prints the correct value. but if I do value.longValue()

again I get the same -300009666327239428 is this of the number being too big and overflowing ?

Community
  • 1
  • 1
Eddie Martinez
  • 13,582
  • 13
  • 81
  • 106
  • 3
    Print `Long.MAX_VALUE` and compare. Yes, it's too big to fit in a `long`. – Tunaki Nov 16 '16 at 15:57
  • so it doesn't throw an exception ? – Eddie Martinez Nov 16 '16 at 15:59
  • 1
    [The documentation](https://docs.oracle.com/javase/8/docs/api/java/lang/Long.html#parseUnsignedLong-java.lang.String-int-) claims that it should throw a `NumberFormatException` if "The value represented by the string is larger than the largest unsigned long, 2^64-1." I'm sorry I don't have an answer though. – tsm Nov 16 '16 at 16:00
  • 1
    @tsm And `Long.MAX_VALUE` is lower than this. Two times lower since a `long` is signed. – Tunaki Nov 16 '16 at 16:03
  • `long` is always signed even if you parse or convert it as if it was unsigned. – Peter Lawrey Nov 16 '16 at 16:39

4 Answers4

5

Java 8 does (somewhat) support unsigned longs, however, you can't just print them directly. Doing so will give you the result that you saw.

If you have an unsigned long

Long number = Long.parseUnsignedLong("FBD626CC4961A4FC", 16);

you can get the correct string representation with the function

String numberToPrint = Long.toUnsignedString(number);

If you now print numberToPrint you get

18146734407382312188

To be more exact, your number is still going to be a regular signed long which is why it shows overflow if printed directly. However, there are new static functions that will treat the value as if it was unsigned, such as this Long.toUnsignedString(long x) or Long.compareUnsigned(long x, long y).

Keiwan
  • 8,031
  • 5
  • 36
  • 49
2

The hexadecimal number "FBD626CC4961A4FC", converted to decimal, is exactly 18146734407382312188. That number is indeed larger than the maximum possible long, defined as Long.MAX_VALUE and which is equal to 263-1, or 9223372036854775807:

System.out.println(new BigInteger("FBD626CC4961A4FC", 16)); // 18146734407382312188
System.out.println(Long.MAX_VALUE);                         // 9223372036854775807

As such, it's normal that you get back a negative number.

You do not have an exception, as it is exactly the purpose of those new *Unsigned* methods added in Java 8, to give the ability to handle unsigned longs (like compareUnsigned or divideUnsigned). Since the type long in Java is still unsigned, those methods work by understanding negative values as values greater than MAX_VALUE: it simulates an unsigned long. parseUnsignedLong says:

An unsigned integer maps the values usually associated with negative numbers to positive numbers larger than MAX_VALUE.

If you print a long that was the result of parseUnsignedLong, and it is negative, all it means is that the value is greater than the max long value as defined by the language, but that methods taking unsigned longs as parameter will correctly interpret those values, as if they were greater than the max value. As such, instead of printing it directly, if you pass that number to toUnsignedString, you'll get the right output, like shown in this other answer. Not all of these methods are new to Java 8, for example toHexString also interprets the given long as an unsigned long in base 16, and printing Long.toHexString(Long.parseUnsignedLong("FBD626CC4961A4FC", 16)) will give you back the right hex String.

parseUnsignedLong will throw an exception only when the value cannot be represented as an unsigned long, i.e. not a number at all, or greater than 264-1 (and not 263-1 which is the maximum value for a signed long).

Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
1

Yes, it overflows when you are trying to print it, as it is converted to Java long type. To understand why let's take log2 of your dec value.

First thing, original value is 18146734407382312188. It's log2 is ~63.9763437545.

Second, look into documentation: in java long type represents values of -2^63 and a maximum value of 2^63-1.

So, your value is obviously greater then 2^63-1, hence it overflows:

-2^63 + (18146734407382312188 - 2^63 + 1) = -300009666327239428

But as @Keiwan brilliantly mentioned, you still can print proper value using Long.toUnsignedString(number);

Andremoniy
  • 34,031
  • 20
  • 135
  • 241
1

Internally unsigned and signed numbers are represented in the same way, i.e. as 8 bytes in case of a long. The difference only how the "sign" bit interpreted, i.e. if you'd do the same in a C/C++ program and store your value into an uint64_t then cast/map it to a asigned int64_t you should get the same result.

Since the maximum value 8 bytes or 64 bits can hold is 2^64-1 that's the hard constraint for such numbers. Also Java doesn't directly support unsigned numbers and thus the only way to store an unsigned long in a long is to allow for a value that's higher than the signed Long.MAX_VALUE. In fact Java doesn't know whether the string/hexcode you're reading is meant to represent a signed or unsigned long so it's up to you to provide that interpretation, either by converting back to a string or using a larger datatype such as BigInteger.

Thomas
  • 87,414
  • 12
  • 119
  • 157