简体   繁体   中英

Java: signed long to unsigned long string

Is there an easy and fast way to convert a Java signed long to an unsigned long string?

-1                    ->  "18446744073709551615"
-9223372036854775808  ->  "09223372036854775808"
 9223372036854775807  ->  "09223372036854775807"
 0                    ->  "00000000000000000000"

Here is a solution using BigInteger:

/** the constant 2^64 */
private static final BigInteger TWO_64 = BigInteger.ONE.shiftLeft(64);

public String asUnsignedDecimalString(long l) {
   BigInteger b = BigInteger.valueOf(l);
   if(b.signum() < 0) {
      b = b.add(TWO_64);
   }
   return b.toString();
}

This works since the unsigned value of a (signed) number in two-s complement is just 2 (number of bits) more than the signed value, and Java's long has 64 bits.

And BigInteger has this nice toString() method which we can use here.

Java 8 includes some support for unsigned longs. If you don't need the zero padding, just do:

Long.toUnsignedString(n);

If you need zero padding, formatting doesn't work for unsigned longs. However this workaround does an unsigned divide by 10 to drop the unsigned value to a point where it can be represented without the sign bit in the long:

String.format("%019d%d", Long.divideUnsigned(n, 10), Long.remainderUnsigned(n, 10));

1

Based on @Paŭlo Ebermann solution I came up with this one:

public static String convert(long x) {
    return new BigInteger(1, new byte[] { (byte) (x >> 56),
        (byte) (x >> 48), (byte) (x >> 40), (byte) (x >> 32),
        (byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8),
        (byte) (x >> 0) }).toString();
}

Using new BigInteger(int signum, byte[] bytes); makes BigInteger to read bytes as positive number (unsigned) and apply signum to it.


2

Based on @Chris Jester-Young solution I found this one:

private static DecimalFormat zero = new DecimalFormat("0000000000000000000");

public static String convert(long x) {
    if (x >= 0) // this is positive
        return "0" + zero.format(x);

    // unsigned value + Long.MAX_VALUE + 1
    x &= Long.MAX_VALUE;
    long low = x % 10 + Long.MAX_VALUE % 10 + 1;
    long high = x / 10 + Long.MAX_VALUE / 10 + low / 10;
    return zero.format(high) + low % 10;
}

3

Yet another way to do it:

private static DecimalFormat zero19 = new DecimalFormat("0000000000000000000");

public static String convert(long x) {
    if (x >= 0) {
        return "0" + zero19.format(x);
    } else if (x >= -8446744073709551616L) {
        // if:   x + 18446744073709551616 >= 10000000000000000000
        // then: x + 18446744073709551616 = "1" + (x + 8446744073709551616)
        return "1" + zero19.format(x + 8446744073709551616L);
    } else {
        // if:   x + 18446744073709551616 < 10000000000000000000
        // then: x + 18446744073709551616 = "09" + (x + 9446744073709551616)
        // so:   9446744073709551616 == -9000000000000000000L
        return "09" + (x - 9000000000000000000L);
    }
}

If you don't want to reinvent the wheel and maintain your code, Guava might be an option:

formatted = UnsignedLong.fromLongBits(myLongValue).toString();
formatted = UnsignedLongs.toString(myLongValue);

References: UnsignedLong , UnsignedLongs

Two years late, but here is a very compact solution that avoids BigInteger and byte arrays.
Basically it emulates unsigned division to extract one digit, and then it offloads the rest to the library function.

public static String unsignedToString(long n) {
    long temp = (n >>> 1) / 5;  // Unsigned divide by 10 and floor
    return String.format("%019d", temp) + (n - temp * 10);
}

Alternatively, if you'd like to avoid temporary strings and library functions altogether, then we can compute all the digits from first principles:

public static String unsignedToString(long n) {
    char[] buffer = new char[20];
    int i = buffer.length - 1;

    // Do first iteration specially
    long temp = (n >>> 1) / 5;  // Unsigned divide by 10
    buffer[i] = (char)(n - temp * 10 + '0');
    n = temp;

    // Do rest of iterations the normal way
    for (i--; i >= 0; i--) {
        buffer[i] = (char)(n % 10 + '0');
        n /= 10;
    }

    return new String(buffer);
}

Both implementations above are functionally equivalent, so you can pick the one you like best.

I also have a non- BigInteger -based version (since having to reach out for BigInteger did bug me for a while); I've retained my main function for your ease of testing:

public class UlongToString {
    private static final String MIN_VALUE = "" + Long.MIN_VALUE;

    public static String ulongToString(long value) {
        long pos = value & Long.MAX_VALUE;
        if (value == pos)
            return String.valueOf(pos);

        char[] chars = MIN_VALUE.toCharArray();
        chars[0] = '0';
        for (int i = chars.length - 1; i != 0 && pos != 0; --i) {
            if ((chars[i] += pos % 10) > '9') {
                chars[i] -= 10;
                ++chars[i - 1];
            }
            pos /= 10;
        }
        int strip = '1' - chars[0];
        return new String(chars, strip, chars.length - strip);
    }

    public static void main(String... args) {
        for (String arg : args) {
            System.out.println(ulongToString(Long.parseLong(arg)));
        }
    }
}

I just had this problem and solved it using this code:

String.format("%016x", x);

I am not sure if I am missing something but it seems a lot simpler this way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM