Объявление неподписанного int в Java


317

Есть ли способ объявить int без знака в Java?

Или вопрос может быть сформулирован так: каков Java-эквивалент unsigned?

Просто чтобы рассказать вам контекст, который я смотрел на реализацию Java String.hashcode(). Я хотел проверить возможность столкновения, если целое число было 32 без знака int.


7
В Java нет неподписанных типов.
Андрей Логвинов

1
Этот пост может помочь вам stackoverflow.com/a/4449161/778687
tusar

2
Похоже, не способ AFAICT. Связанный: stackoverflow.com/questions/430346/…
Джеймс Мэннинг

11
Это зависит от цели, которую вы пытаетесь достичь. Для большинства целей все целые числа в Java подписаны. Однако вы можете рассматривать целое со знаком как беззнаковое в одном конкретном случае: вы можете сдвигать вправо без расширения знака, используя >>>вместо оператора оператор >>.
dasblinkenlight

Ответы:


311

У Java нет типа данных для целых чисел без знака .

Вы можете определить longвместо, intесли вам нужно хранить большие значения.

Вы также можете использовать целое число со знаком, как если бы оно было без знака. Преимущество представления дополнения к двум состоит в том, что большинство операций (таких как сложение, вычитание, умножение и сдвиг влево) идентичны на двоичном уровне для целых чисел со знаком и без знака. Однако некоторые операции (деление, сдвиг вправо, сравнение и приведение) отличаются. Начиная с Java SE 8, новые методы в Integerклассе позволяют полностью использовать intтип данных для выполнения арифметики без знака :

В Java SE 8 и более поздних версиях вы можете использовать тип данных int для представления 32-разрядного целого числа без знака, которое имеет минимальное значение 0 и максимальное значение 2 ^ 32-1. Используйте класс Integer, чтобы использовать тип данных int как целое число без знака. Статические методы , такие как compareUnsigned, и divideUnsignedт.д. , которые были добавлены к классу Integer для поддержки арифметических операций для целых чисел без знака.

Обратите внимание, что intпеременные по-прежнему подписаны при объявлении, но теперь возможна арифметика без знака с использованием этих методов в Integerклассе.


11
Честно говоря, для многих проектов технические требования не так уж строги, и вы действительно можете позволить себе «тратить» такую ​​память.
Симеон Виссер

6
Я знаю, я также понимаю первоначальную цель Java. Но, например, смартфоны не располагают дополнительной памятью. И они обычно используют Java, насколько я знаю. Но я не хочу начинать войну между программистами Java и другими.
Томаш Зато - Восстановить Монику

122
Для меня это не просто случай тратить деньги. Когда вы работаете на небольшом уровне, с unsigned проще работать
Cruncher

24
Начиная с Java 8, это больше не так . В Java SE 8 и более поздних версиях вы можете использовать intтип данных для представления 32-разрядного целого без знака, которое имеет минимальное значение 0и максимальное значение 2^32-1. - см. docs.oracle.com/javase/tutorial/java/nutsandbolts/… и docs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie

2
@ 7SpecialGems: я обновил ответ, чтобы включить эту информацию. При этом невозможно объявить целые числа без знака или исключить отрицательные значения, можно использовать только так, intкак если бы они были без знака, используя различные методы.
Симеон Виссер

70

1
@stas Я испытываю трудности в понимании использования и большую проблему из-за возможности использовать неподписанные типы данных. Из различных источников в Интернете, из которых я читаю, похоже, что оно вращается вокруг просто увеличения максимального значения, и неявное по своей природе гарантирует, что это положительное число. Правильно ли мое понимание, или есть другие важные причины? Кроме того, теперь, когда Integerкласс в Java 8 позволяет использовать unsigned int, это разница между просто пространством и скоростью (поскольку в C / C ++ они примитивны, а в Java - целая обертка объектов)
Abdul

2
@Abdul - когда вы работаете на битовом уровне (обычно потому, что вы взаимодействуете с оборудованием), вам нужно, чтобы значения вели себя определенным образом. то есть - переворачивать после 11111111 до 00000000 и т. д. Использование подписанного вместо неподписанного может нарушить вычисления CRC и т. д. Это не ограничитель показа, просто потраченное время.
Лорн К

3
@Lorne K: в Java intпереворачиваются, даже если они подписаны. Это C / C ++, где неподписанный пролонгируется, но подписанный вызывает «неопределенное поведение» при переполнении. Если «переворачивание» является вашей единственной заботой, вам не нужно подписывать. Наверное, поэтому процедуры CRC и т. Д. Работают на Java без лишних усилий. И именно поэтому новый API добавляет только разбор, форматирование, сравнение, деление и остаток. Все остальные операции, а именно все битовые манипуляции, а также сложение, вычитание, умножение и т. Д., В любом случае, делают правильно.
Хольгер

4
@Ciprian Tomoiaga: для добавления с опрокидыванием битовые комбинации ввода и результата не зависят от того, интерпретируете ли вы его как число со знаком или как число без знака. Если у вас есть терпение, вы можете попробовать его со всеми 2⁶⁵ комбинациями…
Хольгер

3
@ Хольгер спасибо за объяснение! Действительно, получается, именно поэтому мы фактически используем дополнение 2. Я попробовал это с некоторыми комбинациями 2 ^ 8 ^^
Ciprian Tomoiagă

66

То, является ли значение в int знаком или без знака, зависит от того, как интерпретируются биты - Java интерпретирует биты как значение со знаком (у него нет примитивов без знака).

Если у вас есть int, который вы хотите интерпретировать как беззнаковое значение (например, вы читаете int из DataInputStream, который, как вы знаете, содержит беззнаковое значение), то вы можете сделать следующий трюк.

int fourBytesIJustRead = someObject.getInt();
long unsignedValue = fourBytesIJustRead & 0xffffffffl;

Обратите внимание, что важно, чтобы шестнадцатеричный литерал был длинным литералом, а не целым литералом - следовательно, «l» в конце.


3
Для меня это лучший ответ ... Мои данные поступают с UID карты NFC, который может иметь 4 или 8 байтов ... В случае 4 байтов мне нужно было преобразовать его в беззнаковое целое число, и я не мог используйте ByteBuffer.getLong, потому что это были не 64-битные данные. Спасибо.
Луденвье

Почему это должно быть долго. Разве вы не можете просто сделать 0xFFFFFFи сохранить инт?
Displee

19

Нам нужны неподписанные номера модели MySQL, без знака TINYINT, SMALLINT, INT, BIGINTв jOOQ , поэтому мы создали jOOU , минималистичную библиотеку подношение обертки типов для беззнаковых целых чисел в Java. Пример:

import static org.joou.Unsigned.*;

// and then...
UByte    b = ubyte(1);
UShort   s = ushort(1);
UInteger i = uint(1);
ULong    l = ulong(1);

Все эти типы расширяются java.lang.Numberи могут быть преобразованы в примитивные типы высшего порядка и BigInteger. Надеюсь это поможет.

(Отказ от ответственности: я работаю на компанию за этими библиотеками)


Это звучит очень удобно! Спасибо за упоминание. :)
Лукас Соуза

7

Для чисел без знака вы можете использовать эти классы из библиотеки Guava :

Они поддерживают различные операции:

  • плюс
  • минус
  • раз
  • модификация
  • деленное на

В данный момент, похоже, не хватает операторов сдвига байтов. Если вам это нужно, вы можете использовать BigInteger из Java.



2

Возможно, это то, что вы имели в виду?

long getUnsigned(int signed) {
    return signed >= 0 ? signed : 2 * (long) Integer.MAX_VALUE + 2 + signed;
}
  • getUnsigned(0) → 0
  • getUnsigned(1) → 1
  • getUnsigned(Integer.MAX_VALUE) → 2147483647
  • getUnsigned(Integer.MIN_VALUE) → 2147483648
  • getUnsigned(Integer.MIN_VALUE + 1) → 2147483649

Вы жертвуете миллионной секунды времени на выполнение ленивого набора текста с помощью троичных операторов вместо операторов if. Не хорошо. (шучу)
ytpillai

5
Вы действительно думаете, 2 * (long) Integer.MAX_VALUE + 2это легче понять, чем 0x1_0000_0000L? В связи с этим, почему бы не просто return signed & 0xFFFF_FFFFL;?
Хольгер

2

Кажется, что вы можете справиться с проблемой подписания, выполнив «логическое И» над значениями, прежде чем использовать их:

Пример (значение byte[] header[0]is 0x86):

System.out.println("Integer "+(int)header[0]+" = "+((int)header[0]&0xff));

Результат:

Integer -122 = 134

2

Здесь есть хорошие ответы, но я не вижу демонстрации побитовых операций. Как говорит Visser (в настоящее время принятый ответ), Java по умолчанию подписывает целые числа (Java 8 имеет целые числа без знака, но я никогда не использовал их). Без дальнейших церемоний, давайте сделаем это ...

Пример RFC 868

Что произойдет, если вам нужно записать целое число без знака в IO? Практический пример - когда вы хотите вывести время в соответствии с RFC 868 . Для этого требуется 32-разрядное целое число без знака с прямым порядком байтов, которое кодирует количество секунд с 12:00 утра 1 января 1900 года. Как бы вы это закодировали?

Создайте свое собственное 32-разрядное целое число без знака, например:

Объявить байтовый массив из 4 байтов (32 бита)

Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s)

Это инициализирует массив, см. Являются ли байтовые массивы инициализированными в ноль в Java? , Теперь вам нужно заполнить каждый байт в массиве информацией в порядке с прямым порядком байтов (или с прямым порядком байтов, если вы хотите разрушить хаос). Предполагая, что у вас есть long, содержащий время (длинные целые числа имеют длину 64 бита в Java), который называется secondsSince1900(который использует только первые 32 бита, и вы обработали тот факт, что Date ссылается на 12:00 AM 1 января 1970 г.), затем вы можете использовать логическое «И» для извлечения битов из него и смещения этих битов в позиции (цифры), которые не будут игнорироваться при приведении к байту и в порядке с прямым порядком байтов.

my32BitUnsignedInteger[0] = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); // first byte of array contains highest significant bits, then shift these extracted FF bits to first two positions in preparation for coersion to Byte (which only adopts the first 8 bits)
my32BitUnsignedInteger[1] = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
my32BitUnsignedInteger[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
my32BitUnsignedInteger[3] = (byte) ((secondsSince1900 & 0x00000000000000FFL); // no shift needed

Наша my32BitUnsignedIntegerтеперь эквивалентна 32-разрядному целому числу с прямым порядком байтов без знака, которое соответствует стандарту RCF 868. Да, длинный тип данных подписан, но мы проигнорировали этот факт, потому что предполагали, что секундаSince1900 использовала только младшие 32 бита). Из-за перевода длинного в байт все биты выше 2 ^ 7 (первые две цифры в шестнадцатеричном формате) будут игнорироваться.

Источник ссылки: Java Network Programming, 4-е издание.


1

Только что сделал этот кусок кода, который конвертирует this.altura из отрицательного числа в положительное. Надеюсь, что это помогает кому-то нуждающемуся

       if(this.altura < 0){    

                        String aux = Integer.toString(this.altura);
                        char aux2[] = aux.toCharArray();
                        aux = "";
                        for(int con = 1; con < aux2.length; con++){
                            aux += aux2[con];
                        }
                        this.altura = Integer.parseInt(aux);
                        System.out.println("New Value: " + this.altura);
                    }

-19

Вы можете использовать функцию Math.abs (число). Возвращает положительное число.


12
Ниппик: нет, если ты проходишь мимоMIN_VALUE
Деннис Мэн

2
@ kyo722 Я не могу представить, что это вернет положительное значение в диапазоне беззнаковых примитивов.
Флориан Р. Кляйн

1
Ниппик # 2: нет, если вы пройдете0
genisage
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.