Java: длинная длинная строка без знака
Есть ли простой и быстрый способ конвертировать Java, подписанный long в строку без знака?
-1 -> "18446744073709551615"
-9223372036854775808 -> "09223372036854775808"
9223372036854775807 -> "09223372036854775807"
0 -> "00000000000000000000"
Ответы
Ответ 1
Вот решение с помощью 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()
метод, который мы можем использовать здесь.
Ответ 2
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 считывает байты как положительное число (без знака) и применяет к нему 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);
}
}
Ответ 3
Если вы не хотите изобретать колесо и поддерживать свой код, Guava может быть вариантом:
formatted = UnsignedLong.fromLongBits(myLongValue).toString();
formatted = UnsignedLongs.toString(myLongValue);
Ссылки: UnsignedLong, UnsignedLongs
Ответ 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);
}
Обе реализации выше функционально эквивалентны, поэтому вы можете выбрать тот, который вам больше нравится.
Ответ 5
Java 8 включает некоторую поддержку беззнаковых длин. Если вам не нужна нулевая прокладка, просто выполните:
Long.toUnsignedString(n);
Если вам требуется нулевое заполнение, форматирование не работает для long long без знака. Однако это обходное решение делает беззнаковое разделение на 10, чтобы опустить значение без знака в точку, где он может быть представлен без знакового бита в long:
String.format("%019d%d", Long.divideUnsigned(n, 10), Long.remainderUnsigned(n, 10));
Ответ 6
У меня также есть версия, отличная от 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)));
}
}
}
Ответ 7
У меня была эта проблема и она была решена с помощью этого кода:
String.format("%016x", x);
Я не уверен, что мне что-то не хватает, но кажется, что это намного проще.