Java Byte [] to int (Big Endian) с использованием << Weirdness

1

Предположим, что у нас есть следующий байт [4]:

44 a4 8a c6

Итак, что случилось со следующим кодом:

public static int asIntBigEndian(byte[] raw, int offset){
int result = 0;
for(int i=offset; i<offset+4; ++i){
    result = (result << 4) | raw[i]; 
}
return result;
}

Результатом вызова asIntBigEndian (raw, 0) является:

ff ff ff e6

Я заметил, что если бы я прочитал первый байт и распечатал его, я получу:

44

Я бы получил тот же результат, если бы я это сделал:

System.out.println(Integer.toHexString(raw[0] << 24));

0x44000000

Итак, если бы я продолжал логику...

System.out.println(Integer.toHexString( (raw[0] << 24)|(raw[1] << 16) );

0xffa40000

В основном первый байт превратился в 0xff, а второй байт 0xa4 был "xor" в правильное положение. Почему это происходит?

  • 0
    Разве числа не подписаны на Java?
  • 0
    Это недопустимые значения байтов. Пожалуйста, дайте нам минимальный рабочий пример.
Показать ещё 11 комментариев
Теги:
int
endianness
byte

2 ответа

3
Лучший ответ

byte в Java имеет диапазон от -128 (-0x80) до 127 (0x7F). 164 (0xA4) не является допустимым значением, но "A4" - это то, что вы получили, напечатав -92 (-0x5C), как если бы он был без знака.

Преобразование -0x5C в int также дает -0x0000005C. -0x0000005C, напечатанный как unsigned, равен 0xFFFFFFA4.

Другой, возможно, более простой способ думать об этом - думать обо всех значениях как unsigned, но рассматривать преобразование как расширение знака - где верхний бит копируется во все новые биты. Если вы так думаете, 0xA4 является допустимым байтом и (int) 0xA4 равен 0xFFFFFFA4. Тот же результат, более простой мыслительный процесс, но это менее правильный способ думать о числах в Java (не в том, что это имеет значение).

0xFFFFFFA4 << 16 дает 0xFFA40000 и 0x44000000 | 0xFFA40000 0x44000000 | 0xFFA40000 дает 0xFFA40000 - вот как вы получили этот результат.

Исправление прост - вместо raw[i], используйте ((int)raw[i] & 0xFF) или просто (raw[i] & 0xFF) поскольку преобразование в int неявно.

Кроме того, не связанная с этой проблемой (result << 4) должен быть (result << 8). В противном случае вы вычисляете 0x44000 | 0xA400 | 0x8A0 | 0xC6 0x44000 | 0xA400 | 0x8A0 | 0xC6 0x44000 | 0xA400 | 0x8A0 | 0xC6 вместо 0x44000000 | 0xA40000 | 0x8A00 | 0xC6 0x44000000 | 0xA40000 | 0x8A00 | 0xC6 0x44000000 | 0xA40000 | 0x8A00 | 0xC6.

  • 0
    Спасибо за подробное объяснение. Это имеет смысл сейчас!
0

На практике

public static int asIntBigEndian(byte[] raw, int offset){
    ByteBuffer buffer = ByteBuffer.wrap(raw, offset, 4);
    buffer.order(ByteOrder.BIG_ENDIAN);
    return buffer.getInt();
}

Там наверху, но это так просто.

В этом случае в вашем кодовом коде вам может быть лучше подан ByteBuffer.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню