3

If I have a Java long value - say x - which should be interpreted as an unsigned value (i.e. 0x8000_0000_0000_0000 and higher should be interpreted as positive value) then how can I convert it to BigInteger?

Obviously, BigInteger.valueOf(x) would result in a negative value, and conversion to hexadecimals or bytes seems wasteful.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263

2 Answers2

7

Actually, the conversion is pretty simple. You can use masking similar to converting unsigned integers to long:


Let's first create the mask as constant (this simply results in the least significant 32 bits set to 1):

private static final long UNSIGNED_INT_MASK = (1L << Integer.SIZE) - 1L;

then we can perform:

int unsignedInt = 0x8000_0000; // sample input value
long l = (long) unsignedInt & UNSIGNED_INT_MASK;

So for BigInteger we can create the mask like this (64 least significant bits set to 1):

// use "import static java.math.BigInteger.ONE;" to shorten this line
private static final BigInteger UNSIGNED_LONG_MASK = BigInteger.ONE.shiftLeft(Long.SIZE).subtract(BigInteger.ONE);

great, then the rest will be easy:

long unsignedLong = 0x8000_0000_0000_0000L; // sample input value
BigInteger bi =  BigInteger.valueOf(unsignedLong).and(UNSIGNED_LONG_MASK);

It's not rocket science, but sometimes you just want to find a quick and easy answer.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Using [BigInteger(int signum, byte\[\] magnitude)](https://stackoverflow.com/a/55752928/2773647) with conversion long to byte array is faster by 6x on my machine – cybersoft Feb 14 '20 at 16:55
  • There is some real magic (intrinsics) going on in there. I'll give a check to see which one is faster at the moment. Did you test with or without mask generation? – Maarten Bodewes Feb 14 '20 at 23:58
  • I just used your code for BigInteger. `UNSIGNED_LONG_MASK` is left as static field, of course, and the last two lines of code is measured. What else can be improved? (`unsignedLong` is taken from `ThreadLocalRandom`, but I think it doesn't matter) – cybersoft Feb 15 '20 at 08:09
  • Using random in measurements is rather tricky - if it ever blocks then your measure ment is off, and random generation usually uses a heavy cryptographic algorithm. Then there is JVM time before the JIT compilation kicks in. Finally you have the garbage collection for the byte arrays which may only kick in after you've performed testing... This is a subject is not as easy as it may seem. – Maarten Bodewes Feb 15 '20 at 12:56
  • Hmm, link is wrong: [BigInteger(int signum, byte\[\] magnitude)](https://stackoverflow.com/a/10887068/2773647) – cybersoft Feb 17 '20 at 13:12
4

This conversion is actually implemented in java.lang.Long in OpenJDK: Long.java:241. It's private though, so pasting it here:

/**
 * Return a BigInteger equal to the unsigned value of the
 * argument.
 */
private static BigInteger toUnsignedBigInteger(long i) {
    if (i >= 0L)
        return BigInteger.valueOf(i);
    else {
        int upper = (int) (i >>> 32);
        int lower = (int) i;

        // return (upper << 32) + lower
        return (BigInteger.valueOf(Integer.toUnsignedLong(upper))).shiftLeft(32).
            add(BigInteger.valueOf(Integer.toUnsignedLong(lower)));
    }
}

An extensive article on unsigned long and alternatives is available on my blog: Unsigned long in Java

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
aioobe
  • 413,195
  • 112
  • 811
  • 826