Java lê inteiros em little endian ou big endian?

94

Eu pergunto porque estou enviando um fluxo de bytes de um processo C para Java. No lado C, o inteiro de 32 bits tem o LSB é o primeiro byte e MSB é o quarto byte.

Portanto, minha pergunta é: no lado do Java, quando lemos o byte conforme ele foi enviado do processo C, o que é endian no lado do Java?

Uma pergunta de acompanhamento: Se o endian no lado Java não for o mesmo que o enviado, como posso converter entre eles?

Hhafez
fonte
1
Aqui estão meus mnemônicos para isso, então não vou esquecer: Java sendo nenhum hardware, mas virtual, é a linguagem da internet. A ordem de bytes da rede é big endian . Portanto, Java é big endian .
truthadjustr

Respostas:

66

Use a ordem de bytes da rede (big endian), que é a mesma que o Java usa. Veja man htons para os diferentes tradutores em C.

Egil
fonte
Não estou na minha máquina Linux agora, mas o htons é uma das bibliotecas padrão?
hhafez
De acordo com h30097.www3.hp.com/docs//base_doc/DOCUMENTATION/V51_HTML/MAN/… é parte da biblioteca c padrão, sim
Egil
1
htons está disponível em quase todos os lugares, mas não em ISO C.
MSalters
1
Se você tiver que usar algo diferente da ordem de bytes da rede, então você rola seu próprio com operadores bit a bit ou usa as várias versões de java.nio.Buffer
Darron
1
De acordo com sua página de manual, ela é definida em POSIX.1, portanto, deve estar disponível em quase todos os lugares. E parece que me lembro de usá-lo no Win32, então não é apenas em sistemas POSIX.
Joachim Sauer
47

Tropecei aqui via Google e obtive minha resposta que Java é big endian .

Lendo as respostas, gostaria de salientar que os bytes realmente têm uma ordem endian, embora, felizmente, se você só lidou com microprocessadores "convencionais", é improvável que os tenha encontrado como Intel, Motorola e Zilog todos concordaram na direção de mudança de seus chips UART e que MSB de um byte seria 2**7e LSB 2**0em suas CPUs (usei a notação de potência FORTRAN para enfatizar a idade desse material :)).

Eu tive esse problema com alguns dados de downlink serial de bits do ônibus espacial há mais de 20 anos, quando substituímos um hardware de interface de US $ 10 mil por um computador Mac. Há um resumo técnico da NASA publicado sobre isso há muito tempo. Eu simplesmente usei uma tabela de consulta de 256 elementos com os bits invertidos ( table[0x01]=0x80etc.) depois que cada byte foi deslocado do fluxo de bits.

WB Greene
fonte
Ótima percepção! Eu tenho essa pergunta e nenhuma resposta na web.
Xolve de
se algum deles for público, você poderia vincular o brief de tecnologia da NASA (e talvez os dados de downlink serial do ônibus espacial) de que está falando? seria fascinante, nunca vi uma coisa assim.
n611x007
3
Endianness bit a bit também entra em jogo com formatos de compressão que usam alguma forma de codificação Huffman (ou seja, todos eles). Para diversão extra, JPEG é "bitwise big-endian" (ou seja, o bit mais significativo é o bit "primeiro") e LZ é "bitwise little-endian". Certa vez, trabalhei em um formato de compactação proprietário que usava os dois formatos nos bastidores. Oh, isso foi divertido ...
user435779
Tendo começado aos poucos, pensei que ISSO fosse fim por muito tempo.
Roy Falk
20

Não há inteiros sem sinal em Java. Todos os inteiros são assinados e em big endian.

No lado C, cada byte tem o LSB no início à esquerda e o MSB no final.

Parece que você está usando LSB como a parte menos significativa, não é? LSB geralmente significa byte menos significativo. Endianness não é baseado em bits, mas em bytes.

Para converter de um byte sem sinal em um inteiro Java:

int i = (int) b & 0xFF;

Para converter de little-endian de 32 bits não assinado em byte [] para Java long (do topo da minha cabeça, não testado):

long l = (long)b[0] & 0xFF;
l += ((long)b[1] & 0xFF) << 8;
l += ((long)b[2] & 0xFF) << 16;
l += ((long)b[3] & 0xFF) << 24;
Jonas Elfström
fonte
acabei de perceber que: $ então como devo enviar este pequeno endian não assinado ao meu processo java para lê-lo corretamente?
hhafez
O que quero dizer com início é que lsb está no início dos 4 bytes (é um int de 32 bits sem sinal), então eu quis dizer byte menos significativo
hhafez
Também estou convertendo de C -> Java não de Java -> C :)
hhafez
Seu código funciona bem, desde que você remova o ponto-e-vírgula após 0xFF nas últimas três linhas. Eu mesmo editaria, mas isso é uma mudança de menos de 6 caracteres.
Moose Morals
1
Demorou quase 8 anos, mas finalmente alguém percebeu o erro de sintaxe. Obrigado @MooseMorals :)
Jonas Elfström
12

Não há como isso influenciar qualquer coisa em Java, uma vez que não há uma maneira (não-API direta) de mapear alguns bytes diretamente em um int em Java.

Cada API que faz isso ou algo semelhante define o comportamento com bastante precisão, portanto, você deve consultar a documentação dessa API.

Joachim Sauer
fonte
3
Oh, claro que existe. A matemática binária (&, |, <<, etc) funciona bem em bytes e ints. É muito fácil pegar bytes arbitrários e colocá-los em um inteiro.
Herms
8
Mas, se você fizer isso, ainda não conseguirá saber quais extensões sua JVM usa internamente.
Darron
4
Sim, mas mesmo aí você não está mapeando diretamente. Você está usando aritmética que faz exatamente o que você diz, não há ambigüidade. Em C, você sempre pode converter um "byte *" em um "long *" e retirá-lo de referência. Então você teria que se preocupar com endianess. Em Java, não há uma maneira direta e ambígua de fazer isso.
Joachim Sauer
Ah, entendo. Você estava falando sobre o elenco, não sobre a matemática binária. Sim, nesse caso você está certo.
Herms
10
+1 para "procurar a documentação", mas NOTA: a 1ª frase não está mais correta, pois hoje em dia o pacote NIO oferece ByteBuffer que pode mapear bytes para primitivos e onde você pode alterar a ordem dos bytes. Consulte ByteBuffer e ByteOrder
user85421
3

Eu lia os bytes um por um e os combinava em um valor longo . Dessa forma, você controla o endianismo e o processo de comunicação é transparente.

Wouter Lievens
fonte
Importa-se de comentar por que você está votando contra mim?
Wouter Lievens
porque mesmo se eu lesse cada byte individualmente, o fim do byte enviado estaria incorreto, então eu precisaria convertê-lo
hhafez
23
Endianness de um byte? Que diabo é isso? Palavras são sensíveis ao endianismo, bytes individuais não.
Wouter Lievens
3
@hhafez Isso não é verdade, bytes não têm fim, pelo que precisamos nos preocupar se você lê byte por byte, você, o programador, é responsável por atribuir os bytes ao lugar apropriado. Isso é exatamente o que DataInputStream faz, ele apenas reúne os bytes em uma forma big endian sob o capô.
nos
2
@WouterLievens: Encontrei alguns dispositivos de E / S (por exemplo, um chip de relógio em tempo real) que, por alguma razão, enviam dados em formato de bit reverso; após receber os dados deles, é necessário reverter os bits em cada byte. Eu concordo com você, porém, que o endian-ness de bytes geralmente não é um problema, a menos que seja necessário lidar com peças de hardware com design estranho.
supercat
3

Se ele se encaixa no protocolo que você usa, considere usar um DataInputStream, onde o comportamento é muito bem definido .

Ilja Preuß
fonte
1
Ele só pode fazer isso se seu protocolo usar o mesmo endianismo.
Wouter Lievens
Corrigi o link e alterei-o para apontar para Java 9, a versão atual. A API em questão foi introduzida em Java 1.0, no entanto.
Jens Bannmann de
2

Java é 'Big-endian' conforme observado acima. Isso significa que o MSB de um int está à esquerda se você examinar a memória (pelo menos em uma CPU Intel). O bit de sinal também está no MSB para todos os tipos inteiros Java.
Ler um inteiro sem sinal de 4 bytes de um arquivo binário armazenado por um sistema 'Little-endian' requer um pouco de adaptação em Java. O readInt () de DataInputStream espera o formato Big-endian.
Aqui está um exemplo que lê um valor sem sinal de quatro bytes (conforme exibido pelo HexEdit como 01 00 00 00) em um inteiro com um valor de 1:

 // Declare an array of 4 shorts to hold the four unsigned bytes
 short[] tempShort = new short[4];
 for (int b = 0; b < 4; b++) {
    tempShort[b] = (short)dIStream.readUnsignedByte();           
 }
 int curVal = convToInt(tempShort);

 // Pass an array of four shorts which convert from LSB first 
 public int convToInt(short[] sb)
 {
   int answer = sb[0];
   answer += sb[1] << 8;
   answer += sb[2] << 16;
   answer += sb[3] << 24;
   return answer;        
 }
Donald W. Smith
fonte
A que se refere "anotado acima"? A ordem em que as respostas do SO são exibidas pode variar.
LarsH
0

java realmente força big endian: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.11

user12482548
fonte
3
Trata-se de endianness das instruções de bytecode, não de endianness dos dados em tempo de execução.
kaya3
Estou votando. Este trecho byte[] bbb = ByteBuffer.allocate(4).putFloat(0.42f).array();produziu um bytearray que é o reverso do que eu C/C++produzi. Portanto, o big endianness do Java tem efeito até mesmo nos dados em tempo de execução.
truthadjustr