Java:有符号长整型转无符号长整型字符串

23

是否有一种简单快捷的方法将Java有符号long转换为无符号long字符串?

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

2
@parsifal http://en.wikipedia.org/wiki/Twos_complement@parsifal http://zh.wikipedia.org/wiki/二補數 - Ali Shakiba
7个回答

29

以下是使用 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();
}

这段代码之所以有效,是因为在二进制补码中,带符号的数字的无符号值比有符号值多2的n次方。而Java的long类型有64位。

BigInteger类有一个很好用的toString()方法,我们可以在这里使用它。


12

Java 8 支持无符号长整型。如果您不需要零填充,只需执行:

Long.toUnsignedString(n);

如果您需要进行零填充,那么对于无符号长整型来说,格式化方法不起作用。但是,以下解决方法将通过对无符号数值进行除以 10 的操作,将其降低到可以在长整型中表示而不使用符号位的位置:

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

6

1

基于 @Paŭlo Ebermann 的解决方案,我想出了这个:

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();
}

使用new BigInteger(int signum, byte[] bytes);可以使BigInteger将字节作为正数(无符号)读取,并对其应用符号。


2

根据@Chris Jester-Young的解决方案,我找到了这个:

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

另一种方法如下:


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);
    }
}

我很想看看“无BigInteger”版本是否比两个版本都更快。 :-) (如果我今天有时间,我会进行一些测试并发布我的结果。) - C. K. Young
@Chris 看看我的无 BigInteger 版本! :) - Ali Shakiba
+1 非常好(关于除以10的问题)。那可能是最直接的方法。 - C. K. Young
我曾考虑过写类似于你的第一个版本,但对于我的懒惰来说太复杂了 :-) - Paŭlo Ebermann

4

虽然有两年的延迟,但这里提供了一种非常紧凑的解决方案,避免使用BigInteger和字节数组。
基本上它模拟无符号除法来提取一个数字,然后将其余部分转移到库函数中。

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);
}

或者,如果您想要完全避免使用临时字符串和库函数,那么我们可以从头开始计算所有的数字:

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);
}

以上两种实现功能相同,因此您可以选择自己喜欢的方法。


4
如果你不想重复造轮子并且想要维护你的代码,Guava可能是一个选择:
formatted = UnsignedLong.fromLongBits(myLongValue).toString();
formatted = UnsignedLongs.toString(myLongValue);

参考资料:UnsignedLongUnsignedLongs


2

我还有一个基于非BigInteger的版本(因为一段时间内需要使用BigInteger让我感到很烦); 我保留了main函数以方便测试:

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)));
        }
    }
}

-1
我刚遇到了这个问题,并使用以下代码解决了它:
String.format("%016x", x);

我不确定是否有遗漏,但这种方式似乎简单得多。


那么对于-1l,它输出什么? - Ali Shakiba
我刚意识到原帖作者需要一个十进制字符串而不是十六进制字符串 - 我的错! - Karl Dudfield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接