Чи можемо ми зробити Java без підпису на байті


185

Я намагаюся перетворити підписаний байт у безпідписаний. Проблема полягає в тому, що дані, які я отримую, не підписані, і Java не підтримує безпідписаний байт, тому, коли він читає дані, він трактує їх як підписані.

Я спробував перетворити його за допомогою наступного рішення, яке я отримав від Stack Overflow.

public static int unsignedToBytes(byte a)
{
    int b = a & 0xFF;
    return b;
}

Але коли він знову перетворений в байт, я отримую ті самі підписані дані. Я намагаюся використовувати ці дані як параметр функції Java, яка приймає лише байт як параметр, тому я не можу використовувати будь-який інший тип даних. Як я можу виправити цю проблему?


2
Guava: UnsignedBytes.toint (значення байта)
jacktrades

20
java.lang.Byte.toUnsignedInt (значення байта);
тематика

Відповіді:


107

Я не впевнений, що розумію ваше запитання.

Я тільки що спробував це, і для байта -12 (підписане значення) він повернув ціле число 244 (що еквівалентно неподписаному байтовому значенню, але введено як an int):

  public static int unsignedToBytes(byte b) {
    return b & 0xFF;
  }

  public static void main(String[] args) {
    System.out.println(unsignedToBytes((byte) -12));
  }

Це те, що ти хочеш зробити?

Java не дозволяє виразити 244 як byteзначення, як це було б C. Для вираження додатних цілих чисел вище Byte.MAX_VALUE(127) ви повинні використовувати інший цілий тип, наприклад short, intабо long.


1
byte b = (byte)unsignedToBytes((byte) -12); тепер спробуйте надрукувати b
Jigar Joshi

101
Чому ви прийняли це як правильну відповідь? Все, що вона робить, точно так само, як і метод, який ви згадуєте у своєму запитанні, - перетворити байт у ціле число, яке не підписується.
Адамські

1
Важливо іноді мати підписані значення, іноді без підпису, тому, ймовірно, саме тому він прийняв цю відповідь. (байт) (b & 0xff) не має жодного сенсу, але (байт) (Math.min ((b & 0xff) * 2, 255)) має сенс, наприклад, в комп'ютерній графіці він просто зробить піксельне зображення, представлене байт у два рази яскравіший. :-)
iirekm

3
Це також можна назвати byteToUnsigned
Ернан Еш

195

Те, що примітиви підписані на Java, не має значення для того, як вони представлені в пам'яті / транзиті - байт - це лише 8 біт, і чи ви інтерпретуєте це як діапазон підписів чи ні. Немає чарівного прапора, який би сказав "це підписано" або "це не підписано".

Після підписання примітивів компілятор Java заважатиме призначити байт значення, що перевищує +127 (або нижче -128). Однак, ніщо не заважає вам опустити інт (або короткий), щоб досягти цього:

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)

/*
 * Will print a negative int -56 because upcasting byte to int does
 * so called "sign extension" which yields those bits:
 * 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
 *
 * But you could still choose to interpret this as +200.
 */
System.out.println(b); // "-56"

/*
 * Will print a positive int 200 because bitwise AND with 0xFF will
 * zero all the 24 most significant bits that:
 * a) were added during upcasting to int which took place silently
 *    just before evaluating the bitwise AND operator.
 *    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
 * b) were set to 1s because of "sign extension" during the upcasting
 *
 * 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
 * &
 * 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
 * =======================================
 * 0000 0000 0000 0000 0000 0000 1100 1000 (200)
 */
System.out.println(b & 0xFF); // "200"

/*
 * You would typically do this *within* the method that expected an 
 * unsigned byte and the advantage is you apply `0xFF` only once
 * and than you use the `unsignedByte` variable in all your bitwise
 * operations.
 *
 * You could use any integer type longer than `byte` for the `unsignedByte` variable,
 * i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
 * it would get casted to `int` anyway.
 */
void printUnsignedByte(byte b) {
    int unsignedByte = b & 0xFF;
    System.out.println(unsignedByte); // "200"
}

5
Для багатьох операцій це не робить ніякої різниці, однак для деяких операцій - це. У будь-якому випадку ви можете використовувати байт як неподписаний або використовувати char, який не підписаний.
Пітер Лорі

62
Доступ до масиву з потенційно негативним числом не має значення.
Стефан

3
@Stefan - Я мав на увазі неактуальне в контексті того, як вони представлені на дроті.
Адамські

6
Що дещо не стосується питання. Оскільки він зазначив, що йому потрібно передати його функції, яка приймає лише параметри байтів, це не має значення погоди, ми трактуємо це як байтне представлення єдинорога. Java завжди сприйме це як підписане число, що може бути проблематичним для прикладу, коли ця функція використовує параметр як індекс. Однак, щоб бути справедливим, я також спростував інші два найкращі відповіді, оскільки вони також не відповідають на питання.
Стефан

2
@Stefan +1 для вас. Абсолютно актуально, якщо ви використовуєте байт для доступу до масиву з 256 елементів. Це прекрасний приклад, який демонструє, чому всі повинні почати вивчати C та C ++ перед тим, як перейти на Java або C #
Gianluca Ghettini

46

Повне керівництво по роботі з непідписаними байтами на Java:

Непідписаний байт на Java

(Джерело для цієї відповіді.)


Мова Java не містить нічого подібного до unsignedключового слова. A byteвідповідно до мовної специфікації представляє значення від −128 - 127. Наприклад, якщо a byteпередано intJava, інтерпретується перший біт як розширення знака та використання знака .

Незважаючи на це, ніщо не заважає вам розглядати byteпросто як 8 біт і інтерпретувати ці біти як значення між 0 і 255. Просто пам’ятайте, що ви нічого не можете зробити, щоб змусити інтерпретувати чужий метод. Якщо метод приймає a byte, то цей метод приймає значення від −128 до 127, якщо прямо не вказано інше.

Ось кілька корисних перетворень / маніпуляцій для вашої зручності:

Конверсії в / з int

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value

// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(Або, якщо ви перебуваєте на Java 8+, використовуйте Byte.toUnsignedInt.)

Розбір / форматування

Найкращим способом є використання перерахованих вище конверсій:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

Арифметика

Представлення з двома доповненнями "просто працює" для додавання, віднімання та множення:

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;

byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

Ділення вимагає ручного перетворення операндів:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));

1
'char' не означає число.
вихід з системи

26
Якщо коротко: Ви помиляєтесь .
aioobe

36

У Java немає примітивних неподписаних байтів. Звичайна річ - це передати її більшому типу:

int anUnsignedByte = (int) aSignedByte & 0xff;

Чи потрібен акторський склад для int?
nich

Це може бути неявний акторський склад, але є акторський склад в будь-якому випадку. І цей склад робить підписане розширення. І це проблема. Якщо ви робите чіткий склад, ви можете принаймні побачити, що це відбувається.
foo

21

Я думаю, що інші відповіді охоплюють представлення пам’яті, і те, як ви їх вирішите, залежить від контексту того, як ви плануєте використовувати його. Додам, що Java 8 додала деяку підтримку для роботи з неподписаними типами . У цьому випадку ви могли б скористатисяByte.toUnsignedInt

int unsignedInt = Byte.toUnsignedInt(myByte);

4

Бічна примітка, якщо ви хочете роздрукувати її, ви можете просто сказати

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));

6
чому так складно? println(b & 0xff)достатньо
phuclv


0

Адамський дав найкращу відповідь, але вона не зовсім повна, тому прочитайте його відповідь, оскільки вона пояснює деталі, які я не знаю.

Якщо у вас є системна функція, яка вимагає передавати їй непідписаний байт, ви можете передати підписаний байт, оскільки він автоматично трактуватиме його як непідписаний байт.

Отже, якщо для системної функції потрібні чотири байти, наприклад, 192 168 0 1 як непідписані байти, ви можете передати -64 -88 0 1, і функція все одно буде працювати, оскільки акт передачі їх у функцію скасує їх .

Однак у вас навряд чи виникне ця проблема, оскільки системні функції приховані за класами для сумісності між платформами, хоча деякі з методів читання java.io повертають недисципліновані байти як int.

Якщо ви хочете, щоб це працювало, спробуйте записати підписані байти у файл і прочитайте їх як неподписані байти.


1
Не існує такого поняття, як підписані або неподписані байти.
Властиміл Овчачик

Як саме ви писали та читали байти у своєму прикладі?
Властиміл Овчачик

0

Ви також можете:

public static int unsignedToBytes(byte a)
{
    return (int) ( ( a << 24) >>> 24);
}    

Пояснення:

скажімо a = (byte) 133;

У пам'яті він зберігається як: "1000 0101" (0x85 у шістнадцятковій)

Отже, його представлення перекладається без знака = 133, підпис = -123 (як доповнення 2)

a << 24

Коли зсув ліворуч виконується 24 біта вліво, результат тепер - це 4-байтне ціле число, яке представлено у вигляді:

"10000101 00000000 00000000 00000000" (або "0x85000000" у шістнадцятковій кількості)

то ми маємо

(a << 24) >>> 24

і він знову зміщується праворуч на 24 біта, але заповнює провідні нулі. Отже, це призводить до:

"00000000 00000000 00000000 10000101" (або "0x00000085" у шістнадцятковій кількості)

і це неподписане представлення, яке дорівнює 133.

Якщо ви спробували зробити це, a = (int) a; то, що трапиться, це зберігає представлення байту 2 у доповненні і зберігає його як int також як доповнення 2:

(int) "10000101" ---> "11111111 11111111 11111111 10000101"

І це перекладається як: -123


2
У 2019 році це зайве. Просто використовуйте java.lang.Byte.toUnsignedInt(byte value). І якщо ви ще не використовуєте Java 8, оновіть ASAP. Java 7 і новіші версії закінчуються.
Stephen C

0

Я намагаюся використовувати ці дані як параметр функції Java, яка приймає лише байт як параметр

Це істотно не відрізняється від функції, що приймає ціле число, якому потрібно передати значення, більше 2 ^ 32-1.

Це звучить так, як це залежить від того, як визначена та задокументована функція; Я бачу три можливості:

  1. Це може явно задокументувати, що функція розглядає байт як непідписане значення, і в цьому випадку функція, ймовірно, повинна робити те, що ви очікуєте, але, здається, буде реалізовано неправильно. Для цілого випадку функція, ймовірно, оголошує параметр цілим числом без підпису, але це неможливо для випадку байта.

  2. Він може задокументувати, що значення для цього аргументу повинно бути більше (або, можливо, рівне) нулю; у цьому випадку ви неправильно використовуєте функцію (передаючи параметр поза діапазоном), очікуючи, що вона зробить більше, ніж було призначено для робити. З деяким рівнем підтримки налагодження ви можете очікувати, що функція викине виняток або провалить твердження.

  3. Документація нічого не може сказати, у цьому випадку негативний параметр - це негативний параметр, і чи має це значення якесь значення, залежить від того, що функція виконує. Якщо це безглуздо, можливо, цю функцію справді слід визначити / задокументувати як (2). Якщо це має значення без очевидного способу (наприклад, негативні значення використовуються для індексації в масив, а негативні значення використовуються для індексації назад з кінця масиву, так -1 означає останній елемент), документація повинна сказати, що це значить, і я б очікував, що це все одно не те, що ви хочете.


Гммм, я думаю, я щойно опублікував відповідь, яка була призначена для іншого питання про підписаність байтів, але я припускаю, що це ще трохи актуально і тут ...
Кевін Мартін,

-1

Якщо у вас є функція, яка повинна бути передана підписаним байтом, що ви очікуєте, що вона буде виконана, якщо ви передасте байт, який не підписаний?

Чому ви не можете використовувати будь-який інший тип даних?

Як правило, байт можна використовувати як безпідписаний байт з простим або без перекладу. Все залежить від того, як він використовується. Вам потрібно буде уточнити, що ви робите з цим.


-1

Хоча це може здатися прикрою (походить від C), що Java не включила в мову непідписаний байт, але насправді це не велика справа, оскільки проста операція "b & 0xFF" дає значення без підпису (підписаний) байт b у (рідкісному) ситуації, які насправді потрібні. Біти насправді не змінюються - лише інтерпретація (що важливо лише при виконанні, наприклад, деяких математичних операцій зі значеннями).


подивіться, як відповідають інші, ви вважаєте, що ваша відповідь найкраща / корисна? опишіть небагато і додайте це в коментарях
Jubin Patel

8
Це не рідкість лише тому, що ви не стикалися з цим. Спробуйте впровадити протокол, і ви натрапите на це мільйон разів. Прикро, що переважна більшість випадків використання, з якими я стикався, стосується байтів, ви хочете мати справу з непідписаними байтами (адже вони байти, а не числа). Божевільна річ у тому, що БУДЬ-яка побітна операція перетворить її на int, а це означає, що будь-які "негативні" значення будуть абсолютно різними значеннями при їх подовженні. Так, ви можете його обійти, маскуючи завжди, але це марна трата часу, процесор і викликає справді незрозумілі помилки, якщо ви забудете.
Thor84no

Я погоджуюся з Thor84no: байти не є числами і не повинні мати знак. З іншого боку, оскільки вони не є числами, ми навіть не повинні мати / використовувати оператори + і -. Використання лише бітових операторів працює нормально, з іншого боку оператори зсуву не працюють так, як хотілося б, і дійсно java сприяє зміщенному байту до int.
користувач1708042

1
@ VlastimilOvčáčík У цьому випадку це буквально неможливо, це хвилююча річ. Ви ВСЕ повторюєте x & 0xFFвсюди, де вам це потрібно, або ви повторюєте щось подібне behaveLikeAnUnsignedByte(x)скрізь. Це потрібно для кожного місця, де ви використовуєте значення байту або байтовий масив, який потрібно не підписати, немає можливого способу уникнути цього повторення. Ви не можете записати реалізацію протоколу, який читає і записує значення байтів лише з одним посиланням на байт-змінну. Ваш спрощений погляд може пояснити, чому вони ніколи не дбали про це виправити.
Thor84no

-1

У Java немає неподписаного байту, але якщо ви хочете відобразити байт, ви можете це зробити,

int myInt = 144;

byte myByte = (byte) myInt;

char myChar = (char) (myByte & 0xFF);

System.out.println("myChar :" + Integer.toHexString(myChar));

Вихід:

myChar : 90

Для отримання додаткової інформації, будь ласка, перевірте: Як відобразити шістнадцяткове / байтне значення на Java .


Не потрібно визначати це самостійно. java.lang.Byte.toUnsignedInt(byte value);для цього існує.
Олександр - Відновіть Моніку

-2

Згідно з обмеженнями в Java, безпідписаний байт майже неможливий у поточному форматі типу даних. Ви можете перейти до деяких інших бібліотек іншої мови для того, що ви реалізуєте, і тоді ви можете викликати їх за допомогою JNI .


Я не думаю, що він хоче зберігати його як підписаний байт. Він отримує його як підписаний байт, і він хоче зберігати його як int, що цілком справедливо. Його проблема полягає в тому, що де б він не отримував введення, представляє значення між 0 і 255 як байт, але Java інтерпретує це як подвійне доповнення підписаного значення, оскільки java не підтримує підписані байти.
Зак

-2

Так і ні. Я копався з цією проблемою. Як я розумію це:

Справа в тому, що java підписала interger від -128 до 127 .. Можна подати неподписаний в java за допомогою:

public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

Якщо ви, наприклад, додасте -12 підписаний номер для неподписання, ви отримаєте 244. Але ви можете знову використовувати це число в підписаному вигляді, воно повинно бути повернуте назад на підписане, і воно буде знову -12.

Якщо ви спробуєте додати 244 до байта Java, ви отримаєте OutOfIndexException.

Ура ..


3
Не потрібно визначати це самостійно. java.lang.Byte.toUnsignedInt(byte value);для цього існує.
Олександр - Відновіть Моніку

-3

Якщо ви хочете непідписаних байтів на Java, просто відніміть 256 від числа, яке вас цікавить. Це дасть два доповнення з від'ємним значенням, яке є бажаним числом у ненаписаних байтах.

Приклад:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

Потрібно використовувати такі брудні хаки при використанні leJOS для програмування цегли NXT .


Ви розумієте, що двійкове значення 255 також становить 1111 1111, тому ніяке віднімання не потрібно, правда?
Нік Уайт

@NickWhite, так, у двійковій формі. Але java використовує коментар 2, де 255 - це не 11111111
XapaJIaMnu

Вибачте, але це просто неправильно. Спробуйте кілька експериментів. Значення в speed_unsignedпідписано. Роздрукуйте його та подивіться. (І - 256тут нічого не досягається.)
Стівен C
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.