是否有一种简单快捷的方法将Java有符号long转换为无符号long字符串?
-1 -> "18446744073709551615"
-9223372036854775808 -> "09223372036854775808"
9223372036854775807 -> "09223372036854775807"
0 -> "00000000000000000000"
是否有一种简单快捷的方法将Java有符号long转换为无符号long字符串?
-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的n次方。而Java的long
类型有64位。
BigInteger
类有一个很好用的toString()
方法,我们可以在这里使用它。
Java 8 支持无符号长整型。如果您不需要零填充,只需执行:
Long.toUnsignedString(n);
如果您需要进行零填充,那么对于无符号长整型来说,格式化方法不起作用。但是,以下解决方法将通过对无符号数值进行除以 10 的操作,将其降低到可以在长整型中表示而不使用符号位的位置:
String.format("%019d%d", Long.divideUnsigned(n, 10), Long.remainderUnsigned(n, 10));
基于 @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将字节作为正数(无符号)读取,并对其应用符号。
根据@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;
}
另一种方法如下:
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虽然有两年的延迟,但这里提供了一种非常紧凑的解决方案,避免使用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);
}
以上两种实现功能相同,因此您可以选择自己喜欢的方法。
formatted = UnsignedLong.fromLongBits(myLongValue).toString();
formatted = UnsignedLongs.toString(myLongValue);
我还有一个基于非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)));
}
}
}
String.format("%016x", x);
我不确定是否有遗漏,但这种方式似乎简单得多。