Search code examples
javajava-7bigintegersigned

Is BigInteger.longValue() guaranteed to preseve the unsigned binary for 2^63 <= x <= 2^64-1?


What I want to do is storing a 64-bit unsigned integer as a Long type, preserving its interval binary representation.

For example, I want to save

2^63-1 = 9223372036854775807 as it is (9223372036854775807) (∵ 2^63-1 <= Long.MAX_VALUE)

and

2^63 = 9223372036854775808 = 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000(2) as -9223372036854775808
because 10000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000(2) is -9223372036854775808's binary representation for the signed 64-bit integer type.

Likewise, I want to save 2^64-1 = 18446744073709551615 as -1
because (unsigned) 18446744073709551615 = 0xFFFFFFFFFFFFFFFF and (signed long) -1 = 0xFFFFFFFFFFFFFFFF

JavaDoc for BigInteger.longValue() says that

Converts this BigInteger to a long. This conversion is analogous to a narrowing primitive conversion from long to int as defined in section 5.1.3 of The Java™ Language Specification: if this BigInteger is too big to fit in a long, only the low-order 64 bits are returned. Note that this conversion can lose information about the overall magnitude of the BigInteger value as well as return a result with the opposite sign.

However, I am not sure it is guaranteed to work as I intended.

Will BigInteger(string).longValue() be enough for my purpose where string is the string representation of the original integer?


Update: I am using JDK 1.7, so Long.parseUnsignedLong() is not available.


Solution

  • In this case, I would just run the code and see what happens.

    println(new BigInteger("2").pow(63).longValue());
    println(new BigInteger("2").pow(63).subtract(new BigInteger("1")).longValue());
    println(new BigInteger("2").pow(64).longValue());
    println(new BigInteger("2").pow(64).subtract(new BigInteger("1")).longValue());
    

    Outputs

    -9223372036854775808
    9223372036854775807
    0
    -1
    

    So yes, BigInteger.longValue() does exactly what you think it does (and according to the reading of the docs, this is the guaranteed behavior. AKA, it is converted to a bit array, and only the low 64 bits are kept and used as long value, ignoring the fact that the 64'th bit is actually the sign bit).