Предположим, что у нас есть следующий байт [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" в правильное положение. Почему это происходит?
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
.
На практике
public static int asIntBigEndian(byte[] raw, int offset){
ByteBuffer buffer = ByteBuffer.wrap(raw, offset, 4);
buffer.order(ByteOrder.BIG_ENDIAN);
return buffer.getInt();
}
Там наверху, но это так просто.
В этом случае в вашем кодовом коде вам может быть лучше подан ByteBuffer.