Подтвердить что ты не робот

Java: длинная длинная строка без знака

Есть ли простой и быстрый способ конвертировать Java, подписанный long в строку без знака?

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

Ответ 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);

Я не уверен, что мне что-то не хватает, но кажется, что это намного проще.