Как использовать битовый сдвиг в Java

Я пытаюсь создать заголовок IP.

Заголовок IP имеет следующие поля: версия, IHL, DSCP и т. д. Я хотел бы заполнить массив байтов, чтобы хранить информацию в байтах.

Однако меня смущает то, что поле «Версия» имеет ширину всего 4 бита. IHL также имеет ширину всего 4 бита. Как мне подогнать значения обоих этих полей для представления в виде байта? Нужно ли делать битовый сдвиг?

Например. Версия = 4, IHL = 5. Мне нужно было бы создать байт, равный 0100 0101 = 45h или 69 десятичных знаков.


person jdie8274j    schedule 05.11.2015    source источник


Ответы (5)


(byte) (4 << 4) | 5

Это сдвигает значение 4 влево, а затем устанавливает младшие 4 бита на значение 5.

  1. 00000100 Значение (4)
  2. 01000000 После сдвига влево на 4 бита (<< 4)
  3. 00000101 Другое значение (5)
  4. 01000101 Результат побитового ИЛИ (|) #2 и #3

Поскольку операнды относятся к типу int (и даже если бы они были значениями byte, они были бы повышены до int, когда над ними воздействовали такие операторы, как |), окончательный результат приведения должен храниться в byte.

Если вы используете значения byte в качестве операндов в любых побитовых операциях, неявное преобразование в int может привести к неожиданным результатам. Если вы хотите обрабатывать byte так, как если бы оно было беззнаковым в этом преобразовании, используйте побитовое И (&):

byte b = -128; // The byte value 0x80, -128d
int uint8 = b & 0xFF; // The int value 0x00000080, 128d
int i = b; // The int value 0xFFFFFF80, -128d
int uintr = (b & 0xFF) | 0x04; // 0x00000084
int sintr = b | 0x04; // 0xFFFFFF84
person erickson    schedule 05.11.2015
comment
byte Version = 5; byte IHL = 4; byte b = (Version << 4) + IHL; компилируется нормально, приведение не требуется - person phflack; 05.11.2015
comment
Спасибо @Erickson - отличный ответ. - person jdie8274j; 05.11.2015
comment
Вероятно, было бы лучше всего использовать только верхнюю часть, прежде чем вводить целые числа в ответ. - person phflack; 05.11.2015
comment
Тогда почему я могу скомпилировать его без ошибок, просто используя байты и без приведения? - person phflack; 05.11.2015
comment
Какой компилятор я должен использовать для тестирования подобных вещей? byte b = (((byte)5) << 4) + ((byte)4); System.out.println(b); работает с Oracle JDK 8u65 для Mac - person phflack; 05.11.2015
comment
@phflack Мой ответ написан с предположением, что некоторые входные данные на самом деле будут переменными. На практике вы бы не написали byte b = (4 << 4) | 5, вы бы просто написали byte b = 0x45;, что идентично с точки зрения компилятора. Но если версия или длина заголовка является переменной (а не константой времени компиляции), такие операции, как + и |, потребуют приведения к byte. - person erickson; 05.11.2015

Вы можете сделать что-то вроде этого:

    int a = 0x04;
    a <<= 4;
    a |= 0x05;
    System.out.println(a);

что по существу превращает 0b00000100 в 0b01000000, а затем в 0b01000101.

person ergonaut    schedule 05.11.2015

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

Чтобы создать компактное поле, содержащее как Version, так и IHL в одном байте, попробуйте сделать

byte b = (byte)((Version << 4) + IHL);

Это будет работать только в том случае, если Version и IHL являются числами от 0 до 15.

person phflack    schedule 05.11.2015
comment
Я использую байты для версии и IHL и не получаю сообщение об ошибке - person phflack; 05.11.2015
comment
<< на самом деле нормально, но + выполняет двоичное преобразование в int или long, в зависимости от самого широкого операнда. С чем вы компилируете? Возможно, вы захотите переключиться, так как этот код недействителен в соответствии с спецификация языка - person erickson; 05.11.2015
comment
@erickson Вероятно, как двухнедельная версия javac и java в Debian (почти уверен, что это Debian) - person phflack; 05.11.2015
comment
1.8.0_25-b17 говорит, error: incompatible types: possible lossy conversion from int to byte, 1.7.0_67-b01 говорит error: possible loss of precision. Eclipse выдал сообщение, показанное выше. У вас также есть цитата из JLS. - person erickson; 05.11.2015
comment
@erickson Попытка воспроизвести, как ему удалось скомпилировать, и обнаружила, что каким-то образом byte b = (5 << 4) + 4; работает, числа могут быть преобразованы как int или как byte и все еще работают, но любое использование реальных переменных нарушает его, а приведение значений не работает. - person phflack; 05.11.2015
comment
Да, если вы используете константы времени компиляции, компилятор фактически вычисляет значение и сохраняет его. Скомпилированный байтовый код точно такой же, как если бы вы написали byte b = 0x54; Однако byte b = (0x08 << 4) | 0x04; не скомпилируется, потому что результат слишком велик для byte. Кроме того, если в выражении используются переменные, как в вашем ответе, это ошибка времени компиляции, потому что результатом правой части является int, согласно JLS. - person erickson; 05.11.2015
comment
Вы хотели исправить свой ответ, чтобы я мог удалить отрицательный голос? - person erickson; 05.11.2015
comment
Это изменилось, и Java действительно странно обрабатывает байты по сравнению с целыми числами. - person phflack; 05.11.2015

Просто потому, что байт состоит из 8 бит, а ваши значения могут быть максимум 4, это не проблема. Дополнительные 4 бита всегда будут нулями.

Итак, если вы хранили 1, например:

0000 0001

или 15 (какое максимальное значение верно?):

0000 1111

person thatidiotguy    schedule 05.11.2015
comment
Такие сомнения должны быть прояснены в разделе комментариев. это не ответ - person Faiz Halde; 05.11.2015
comment
@FaizPinkman Какие сомнения? Я даю четкий ответ, и я не ценю ваш отрицательный голос. Я говорю, просто сохраните его как полный байт. - person thatidiotguy; 05.11.2015
comment
В вопросе говорится, как вписать два значения в один байт - person Faiz Halde; 05.11.2015

Сдвиг байтов в Java невозможен.

Как работает битовый сдвиг в Java?

Однако, что касается логики, если вам нужна версия и IHL в одном байте, вы можете сделать это, используя следующее

byte value = (byte) (IHL | VERSION << 4);
person Faiz Halde    schedule 05.11.2015
comment
Кажется, вы перепутали Java с каким-то другим языком, где char будет эквивалентен байту. - person VGR; 05.11.2015