Можем ли мы сделать неподписанный байт в Java

185

Я пытаюсь преобразовать подписанный байт в неподписанный. Проблема в том, что данные, которые я получаю, не подписаны, а Java не поддерживает неподписанный байт, поэтому, когда он читает данные, он обрабатывает их как подписанные.

Я попытался преобразовать его с помощью следующего решения, полученного из Stack Overflow.

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

Но когда он снова преобразуется в байты, я получаю те же подписанные данные. Я пытаюсь использовать эти данные в качестве параметра функции Java, которая принимает только байт в качестве параметра, поэтому я не могу использовать любой другой тип данных. Как я могу решить эту проблему?

DLN
источник
2
Гуава: UnsignedBytes.toint (в
байтовом
20
java.lang.Byte.toUnsignedInt (байтовое значение);
themarketka

Ответы:

107

Я не уверен, что понимаю ваш вопрос.

Я только что попробовал это и для байта -12 (значение со знаком) он возвратил целое число 244 (эквивалентно значению байта без знака, но напечатано как 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); Теперь попробуйте распечатать б
Джигар Джоши
101
Почему вы приняли это как правильный ответ? Все, что он делает, точно так же, как метод, который вы упоминаете в своем вопросе - конвертировать байт в целое число без знака.
Адамски
1
Иногда важно иметь подписанные значения, иногда без знака, поэтому, вероятно, именно поэтому он принял этот ответ. (byte) (b & 0xff) не имеет никакого смысла, но (byte) (Math.min ((b & 0xff) * 2, 255)) имеет смысл, например, в компьютерной графике он просто сделает пиксел, представленный как байт в два раза ярче. :-)
iirekm
3
Это также можно назвать byteToUnsigned
Hernán Eche
195

Тот факт, что примитивы подписаны в Java, не имеет отношения к тому, как они представлены в памяти / транзите - байт составляет всего 8 бит, и независимо от того, интерпретируете ли вы его как диапазон со знаком или нет, зависит от вас. Там нет волшебного флага, чтобы сказать «это подписано» или «это не подписано».

Поскольку примитивы подписаны, компилятор Java не позволит вам присвоить байту значение больше +127 (или меньше -128). Тем не менее, нет ничего, что могло бы помешать вам уменьшить int (или short) для достижения этой цели:

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
Для многих операций это не делает различий, однако для некоторых операций это делает. В любом случае вы можете использовать байт как неподписанный или использовать символ без знака.
Питер Лори
62
Доступ к массиву с потенциально отрицательным числом не имеет значения.
Стефан
3
@Stefan - я имел в виду неуместно в контексте того, как они представлены на проводе.
Адамски
6
Что несколько не имеет отношения к вопросу. Поскольку он упомянул, что ему нужно передать его в функцию, которая принимает только байтовые параметры, не имеет значения, погоду мы интерпретируем как байтовое представление единорога. Java всегда будет рассматривать его как число со знаком, что может быть проблематично для примера, когда эта функция использует параметр в качестве индекса. Однако, чтобы быть справедливым, я также отклонил другие 2 лучших ответа, так как они также не отвечают на вопрос.
Стефан
2
@ Стефан +1 для тебя. Абсолютно актуально, если вы используете байт для доступа к массиву из 256 элементов. Это отличный пример, чтобы продемонстрировать, почему все должны начать изучать C и C ++, прежде чем переходить на Java или C #
Джанлука Геттини
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));

Арифметика

Представление из 2-х «просто работает» для сложения, вычитания и умножения:

// 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));
aioobe
источник
1
'char' не представляет число.
выход
26
Короче говоря: вы не правы .
2012 года
36

В Java нет примитивных байтов без знака. Обычная вещь, чтобы привести его к большему типу:

int anUnsignedByte = (int) aSignedByte & 0xff;
Питер Кнего
источник
Требуется ли приведение к int?
нич
Это может быть неявное приведение, но в любом случае есть приведение. И этот актерский состав делает подписанное расширение. И это проблема. Если вы делаете явное приведение, вы можете по крайней мере увидеть, что это происходит.
Foo
21

Я думаю, что другие ответы касались представления памяти, и то, как вы справитесь с этим, зависит от контекста того, как вы планируете его использовать. Я добавлю, что в Java 8 добавлена ​​поддержка работы с неподписанными типами . В этом случае вы можете использоватьByte.toUnsignedInt

int unsignedInt = Byte.toUnsignedInt(myByte);
mkobit
источник
4

Примечание: если вы хотите распечатать, вы можете просто сказать

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Кайл Кинкейд
источник
6
почему так сложно? println(b & 0xff)достаточно
phuclv
0

Если вы думаете, что ищете что-то подобное.

public static char toUnsigned(byte b) {
    return (char) (b >= 0 ? b : 256 + b);
}
Тобиас Йоханссон
источник
0

Адамский предоставил лучший ответ, но он не совсем полный, поэтому прочитайте его ответ, так как он объясняет детали, которыми я не являюсь.

Если у вас есть системная функция, которая требует, чтобы ей был передан неподписанный байт, вы можете передать подписанный байт, поскольку он автоматически будет обрабатывать его как неподписанный байт.

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

Однако вряд ли у вас возникнет эта проблема, так как системные функции скрыты за классами для кроссплатформенной совместимости, хотя некоторые из методов чтения java.io возвращают невыгруженные байты в виде целых чисел.

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

Эве луон
источник
1
Нет такой вещи как подписанные или неподписанные байты.
Властимил Овчачик
Как именно вы писали и читали байты в вашем примере?
Властимил Овчачик
0

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

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

Объяснение:

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

В памяти он хранится как: «1000 0101» (0x85 в шестнадцатеричном формате)

Таким образом , ее представление переводит без знака = 133, подписанный = -123 (как дополнение до 2)

<< 24

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

«10000101 00000000 00000000 00000000» (или «0x85000000» в шестнадцатеричном формате)

тогда мы имеем

(<< 24) >>> 24

и он снова сдвигается на правильные 24 бита, но заполняется ведущими нулями. Так что это приводит к:

«00000000 00000000 00000000 10000101» (или «0x00000085» в шестнадцатеричном формате)

и это представление без знака, равное 133.

Если вы попытались выполнить приведение, a = (int) a; то получится, что он сохранит представление байта в дополнении 2 и сохранит его как int, а также дополнение 2:

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

И это переводится как: -123

mark_infinite
источник
2
В 2019 году это не нужно. Просто используйте java.lang.Byte.toUnsignedInt(byte value). И если вы еще не используете Java 8, обновите как можно скорее. Java 7 и более ранние версии являются окончательными.
Стивен С.
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.
user1708042
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 .

Jyo The Whiff
источник
Нет необходимости определять это самостоятельно. java.lang.Byte.toUnsignedInt(byte value);существует для этого.
Александр - Восстановить Монику
-2

Согласно ограничениям в Java, беззнаковый байт почти невозможен в текущем формате типа данных. Вы можете обратиться к некоторым другим библиотекам другого языка для того, что вы реализуете, а затем вы можете вызывать их с помощью JNI .

Притеш Джайн
источник
Я не думаю, что он хочет хранить это как подписанный байт. Он получает его как подписанный байт и хочет сохранить его как int, что вполне допустимо. Его проблема в том, что везде, где он получает входные данные, представляет значение от 0 до 255 в виде байта, но Java интерпретирует это как двойное дополнение к знаковому значению, потому что java не поддерживает подписанные байты.
Зак
-2

Да и нет. Я копался с этой проблемой. Как я понимаю это:

Дело в том, что Java имеет подписанное число от -128 до 127. Можно представить неподписанное в Java с помощью:

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

Например, если вы добавляете число со знаком -12, чтобы оно было без знака, вы получаете 244. Но вы можете использовать этот номер снова в знаке, его нужно перевести обратно в число со знаком, и оно снова будет -12.

Если вы попытаетесь добавить 244 к байту Java, вы получите исключениеOfIndexException.

Ура ..

Синдри Лор
источник
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 .

XapaJIaMnu
источник
Вы понимаете, что двоичное значение 255 также равно 1111 1111, поэтому вычитание не требуется, верно?
Ник Уайт
@NickWhite, да в двоичном виде. Но ява использует
комплемент
Извините, но это просто неправильно. Попробуйте несколько экспериментов. Значение в speed_unsignedподписано. Распечатай и посмотри. (И - 256здесь ничего не получается.)
Стивен С.