Як перетворити Int на непідписаний байт і назад


92

Мені потрібно перетворити число в байт без підпису. Число завжди менше або дорівнює 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

Відповіді:


201

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

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

Я все ще отримую 65 ... Чому це?
darksky

1
Він зберігає потрібний номер у байті. Java просто розглядає його як номер із підписом, а не як номер без підпису. Але кожен біт такий самий, як ніби це був номер без підпису. Ваше перетворення рядків у байти - це ще одне, не пов’язане питання. Опублікуйте це окремо, у новому запитанні.
JB Nizet

1
використовуйте getInt, щоб повернути int, і перетворіть int як байт. Int - це int. Звідки це береться, не має значення.
JB Nizet

19
Чому побітове та з 0xFFрезультатом ціле число без знака? Деякі пояснення були б дуже корисними.
user462455

2
Java 8 використовує той самий метод: public static int toUnsignedInt (байт x) {return ((int) x) & 0xff; }
Даніель Де Леон,

55

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

Загалом, Java 8 надає методи перетворення byteі shortв unsigned intі long, і intв unsigned 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-бітового беззнакового значення, буде a short.


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, я оновив свою відповідь саме тоді, коли ти писав свій коментар :-)
Péter Török

Добре дякую! З якихось причин він все-таки роздруковує 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();

Особливо корисно для роботи з форматами двійкових даних (наприклад, плоскі формати повідомлень тощо)


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це все одно буде, -22тому що кастинг зберігає знак числа.

1{24}11101010

Відливають 32-bitзначення bвиконання andоперації з 0xFF.

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

Тоді ви отримаєте 234як відповідь.


1

Обробка байтів і цілих чисел без знака за допомогою 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

Незважаючи на те, що вже пізно, я хотів би дати свій внесок щодо цього, оскільки це може пояснити, чому працює рішення, надане JB Nizet. Я натрапив на цю маленьку проблему, працюючи над байтовим синтаксичним аналізатором та перетворюючи рядки сам. Коли ви копіюєте з інтегрального типу більшого розміру, на інтегральний тип меншого розміру, як сказано в цьому документі Java, це трапляється:

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

Ви можете бути впевнені, що байт є інтегральним типом, оскільки в цьому Java-документі написано https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html байт: Тип даних байта - це 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 лише для цілих чи довгих?


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));
}

-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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.