Можно ли назначить 4 байта с порядком байтов в порядке байтов для целого числа без знака примитиву Java, используя только побитовые операторы? - PullRequest
1 голос
/ 30 сентября 2010

Я хочу прочитать 4 байта, которые являются порядком байтов с прямым порядком байтов 32-разрядного целого числа без знака, и присвоить значение Java int

(Да, на самом деле я буду использовать «long», но в этом случае я «знаю», что значение без знака никогда не бывает таким большим, что оно будет переполнено знаком int в нотации дополнения до двух, и это подходит для моей иллюстрации используйте int).

4 байта, о которых идет речь, кодируют значение 216 в стиле little-endian:

0xD8000000

И в основном мне просто нужно вставить следующий битовый шаблон в Java int:

0x000000D8

Следующий простой код должен это сделать ... и для первых трех байтов «0x00» это успешно выполняется:

byte b1 = din.readByte();
byte b2 = din.readByte();
byte b3 = din.readByte();
byte b4 = din.readByte();
int s = 0;
s = s | b4;
s = (s << 8);
s = s | b3;
s = (s << 8);
s = s | b2;
s = (s << 8);
s = s | b1;
return s;

Однако, он облажается:

s = s | b1;

... потому что биты b1 равны 1101 1000, что является отрицательным числом (-40) в двоичной нотации, поскольку наиболее значимый бит равен 1. Когда Java расширяет b1 до целого числа перед оценкой побитового значения или оператор |, -40 кодируется как 0xFFFFFFD8, что порождает наше наивное предположение, что первые 3 байта расширенного целого будут 0.

Так что моя стратегия на мель. Но что мне делать вместо этого? Возможно ли даже решить эту проблему с помощью примитивных операторов (просьба дать решение), или мы должны прибегнуть к библиотеке классов? (Я не слишком много играю с битами и байтами напрямую в моем обычном кодировании, поэтому мне не хватает идиомы для того, что должно быть «повседневным» кодом).

Что касается подхода к библиотеке классов, следующий фрагмент дает правильный результат:

ByteBuffer b = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).put((byte) 0xD8).put((byte) 0x00).put((byte) 0x00).put((byte) 0x00);
b.flip();
int s = b.getInt();

... что хорошо для удобочитаемости, но использует 8 вызовов методов, с которыми я бы лучше обошелся.

спасибо! Дэвид.

1 Ответ

6 голосов
/ 30 сентября 2010

Просто включите & 0xff для каждого байта в продвижение int, чтобы убедиться, что верхние биты установлены в 0:

byte b1 = din.readByte();
byte b2 = din.readByte();
byte b3 = din.readByte();
byte b4 = din.readByte();
int s = 0;
s = s | (b4 & 0xff);
s = (s << 8);
s = s | (b3 & 0xff);
s = (s << 8);
s = s | (b2 & 0xff);
s = (s << 8);
s = s | (b1 & 0xff);
return s;

Или более компактно:

byte b1 = din.readByte();
byte b2 = din.readByte();
byte b3 = din.readByte();
byte b4 = din.readByte();
return ((b4 & 0xff) << 24)
     | ((b3 & 0xff) << 16)
     | ((b2 & 0xff) << 8)
     | ((b1 & 0xff) << 0);

(Очевидно, что «сдвиг влево 0» не нужен, но он поддерживает согласованность выше.)

...