Java:签名长到无符号长字符串

有没有一种简单快捷的方法将Java签名长转换为无符号长字符串?

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

以下是使用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 (比特数) ,而Java的long有64比特。

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

1

根据@PaŭloEbermann解决方案,我想出了这个:

 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读取字节为正数(无符号)并对其应用signum。


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

Java 8包含对unsigned longs的一些支持。 如果您不需要零填充,只需执行以下操作:

 Long.toUnsignedString(n); 

如果您需要零填充,格式化不适用于无符号长整数。 但是,此解决方法使用无符号除以10将无符号值拖放到可以在长整数中没有符号位的情况下表示的值:

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

如果您不想重新发明轮子并维护代码,可以选择番石榴:

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

参考文献: UnsignedLong , UnsignedLongs

两年后,但这是一个非常紧凑的解决方案,避免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); } 

上述两种实现在function上都是等效的,因此您可以选择您最喜欢的那种。

我也有一个非基于BigInteger的版本(因为不得不伸手BigInteger做了一段时间我的错误); 为了方便测试,我保留了我的mainfunction:

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

我刚遇到这个问题并使用此代码解决了它:

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

我不确定我是否遗漏了一些东西,但这种方式似乎更简单。