Как преобразовать Int в беззнаковый байт и обратно

93

Мне нужно преобразовать число в байт без знака. Число всегда меньше или равно 255, поэтому оно умещается в одном байте.

Мне также нужно преобразовать этот байт обратно в это число. Как бы я сделал это на Java? Я пробовал несколько способов, но ни один не работал. Вот что я сейчас пытаюсь сделать:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

а теперь преобразовать этот байт обратно в число:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

Ясно, что это не работает. По какой-то причине он всегда конвертирует число в 65. Какие-либо предложения?

темное небо
источник
Я тоже пробовал такой способ: crazysquirrel.com/computing/java/basics/… - не работает.
darksky

Ответы:

205

Байт всегда подписан в Java. Вы можете получить его беззнаковое значение, заполнив его двоичным кодом с 0xFF:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
источник
Мне все еще 65 ... Почему?
darksky
1
Он хранит желаемое число в байте. Java просто считает его числом со знаком, а не беззнаковым. Но каждый бит такой же, как если бы это было беззнаковое число. Преобразование вашей строки в байты - другой, не связанный с этим вопрос. Выложите отдельно, в новом вопросе.
JB Nizet
1
используйте getInt, чтобы вернуть свой int, и преобразуйте int как байт. Тип int - это int. Откуда это взялось, значения не имеет.
JB Nizet
19
Почему побитовое и с 0xFFприводит к беззнаковому целому числу? Некоторое объяснение было бы действительно полезно.
user462455 03
2
В Java 8 используется тот же метод: public static int toUnsignedInt (byte x) {return ((int) x) & 0xff; }
Даниэль Де Леон
57

Java 8 обеспечивает Byte.toUnsignedIntпреобразование byteв intбеззнаковое преобразование. В Oracle JDK это просто реализовано, return ((int) x) & 0xff;поскольку HotSpot уже понимает, как оптимизировать этот шаблон, но он может быть встроен на других виртуальных машинах. Что еще более важно, не требуется никаких предварительных знаний, чтобы понять, что toUnsignedInt(foo)делает call to .

В целом, Java 8 предоставляет методы для преобразования byteи shortв беззнаковый intи long, и intв беззнаковый long. Метод преобразования byteв беззнаковый shortбыл намеренно опущен, потому что JVM предоставляет только арифметические операции intи в longлюбом случае.

Чтобы преобразовать Int обратно в один байт, просто использовать бросок: (byte)someInt. Результирующее сужающее примитивное преобразование отбрасывает все, кроме последних 8 бит.

Джеффри Босбум
источник
7

Если вам просто нужно преобразовать ожидаемое 8-битное значение из подписанного int в беззнаковое значение, вы можете использовать простой битовый сдвиг:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Если вы используете что-то другое, кроме intбазового типа, вам, очевидно, потребуется отрегулировать величину сдвига: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Также имейте в виду, что вы не можете использовать byteтип, это приведет к значению со знаком, как упоминалось другими респондентами. Наименьшим примитивным типом, который вы могли бы использовать для представления 8-битного беззнакового значения, был бы short.

dm78
источник
3

В Integer.toString(size)новообращенном вызове в полукоксе представление вашего целого числа, т.е. полукокса '5'. ASCII представление этого символа является значение 65.

Вам нужно сначала разобрать строку до целочисленного значения, например, используя Integer.parseInt, чтобы вернуть исходное значение int.

Подводя итог, для преобразования Stringсо знаком / без знака лучше не использовать изображение и использовать битовые манипуляции, как предлагает @JB.

Петер Торок
источник
Ну вот так? (Здесь все еще 65)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
darksky
@Nayefc, я обновил свой ответ так же, как вы писали свой комментарий :-)
Петер Торок
Хорошо, спасибо! По какой-то причине он все еще печатает 65. Что это значит? Если он просто показывает 65, что на самом деле хранится в байте, я этого не понимаю. Также проверьте мой вопрос, я отредактировал его и добавил раздел о преобразовании строк :)
darksky
@Nayefc, опять же, возвращает String.getBytes()значения ASCII символов, содержащихся в тексте, а не числовые значения цифр. Вам нужно разобрать строку в числовое значение, как я намекнул выше.
Péter Török
1
@Nayefc, так что это совсем другой вопрос. И AFAIS это должно работать. Создайте новый вопрос, включая фактический ввод и вывод преобразования, а также полный код для воспроизведения случая.
Péter Török
3

Решение работает нормально (спасибо!), Но если вы хотите избежать кастинга и оставить низкоуровневую работу JDK, вы можете использовать DataOutputStream для записи ваших int и DataInputStream для их чтения обратно. Они автоматически обрабатываются как неподписанные байты тогда:

Для преобразования int в двоичные байты;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Читая их обратно:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. полезен для обработки двоичных форматов данных (например, форматов плоских сообщений и т. д.)

Грегор
источник
3

Кроме того char, все остальные числовые типы данных в Java подписаны.

Как было сказано в предыдущем ответе, вы можете получить значение без знака, выполнив andоперацию с 0xFF. В этом ответе я объясню, как это происходит.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Если ваша машина 32-битная, то intдля хранения значений типу данных нужны 32- битные . byteнужно только 8 бит.

intПеременная iпредставлена в памяти следующим образом (в виде 32-разрядного целого числа).

0{24}11101010

Тогда byteпеременная bпредставлена ​​как:

11101010

Поскольку bytes беззнаковые, это значение представляет -22. (Найдите дополнение до 2, чтобы узнать больше о том, как представлять отрицательные целые числа в памяти)

Тогда, если вы intприведете is к, это все равно будет, -22потому что приведение сохраняет знак числа.

1{24}11101010

Приведенное 32-bitзначение bвыполнения andоперации с 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Тогда вы получите 234ответ.

Рамеш-Х
источник
1

Несмотря на то, что уже слишком поздно, я хотел бы высказать свое мнение по этому поводу, поскольку это может прояснить, почему решение, данное JB Nizet, работает. Я наткнулся на эту небольшую проблему, работая над синтаксическим анализатором байтов и преобразованием строк сам. Когда вы копируете интегральный тип большего размера в интегральный тип меньшего размера, как говорится в этом java-документе, это происходит:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Сужающее преобразование целого числа со знаком в целочисленный тип T просто отбрасывает все, кроме n наименьшего порядка битов, где n - количество битов, используемых для представления типа T. Помимо возможной потери информации о величине числового значения, это может привести к тому, что знак результирующего значения будет отличаться от знака входного значения. .

Вы можете быть уверены, что байт является интегральным типом, как говорится в этом java-документе https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html byte: байтовый тип данных - это 8-битные подписанные два дополнять целое число.

Таким образом, в случае преобразования целого числа (32 бита) в байт (8 бит) вы просто копируете последние (наименее значимые 8 бит) этого целого числа в заданную байтовую переменную.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

Вторая часть истории касается того, как унарные и бинарные операторы Java продвигают операнды. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 Расширяющееся примитивное преобразование (§5.1.2) применяется для преобразования одного или обоих операндов, как указано по следующим правилам:

Если один из операндов имеет тип double, другой преобразуется в double.

В противном случае, если один из операндов имеет тип float, другой преобразуется в float.

В противном случае, если один из операндов имеет тип long, другой преобразуется в long.

В противном случае оба операнда преобразуются в тип int.

Будьте уверены, если вы работаете с целым типом int и / или ниже, он будет повышен до int.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

Я тоже почесал в затылке :). Здесь есть хороший ответ rgettman. Побитовые операторы в Java только для целых и длинных?

KaziQ
источник
0

Если вы хотите использовать примитивные классы-оболочки, это сработает, но все типы java по умолчанию подписаны.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Ромео
источник
0

Обработка байтов и целых чисел без знака с помощью BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
пгранджин
источник
Это преобразуется в ПОДПИСАННОЕ целое число, а не в беззнаковое ...
Педро,
-1

в java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

результат: 254

Тристан Де Смет
источник