Dúvida com bitwise e bitshift

Pessoal,

me tirem uma dúvida, estou com um 2 métodos que fazem o seguinte processo: 1 método converte um array de bytes para um Long e o outro converte de um Long para um array de bytes, até o momento o método tem funcionado Ok, no entanto apresentou um problema na hora da conversão quando for um número negativo, ou seja, ele só funciona bem quando é número positivo, já olhei, reolhei e não consigo achar a falha, se alguém poder ajudar, dando uma luz… segue os métodos:

byteArrayToLong:

/**
	 * Converts from byte array to long.
	 * @param b - byte array
	 * @param size - the length
	 * @return
	 */
	public static long byteArrayToLong(byte[] b, Integer size, boolean autoLength){
		int length = 0;
		if (autoLength){
			length = b.length;
		}else{
			length = size;
		}
		long ourLong = 0;
		for (int i = 0; i < length; i++){
			ourLong |= b[i] & 0xFF;
			if (i+1 < length)	ourLong <<= 8;
		}
		return ourLong;
	}

longToByteArray:

/**
	 * Converts long in byte array with the given size.
	 * @param l - the long value
	 * @param size - the size (length) to array.
	 * @return a byte[].
	 */
	public static byte[] longToByteArray(long l, int size){
		byte[] bytes = new byte[size];
		int p = 0;
		for (int i = size-1; i >= 0; i--){
			bytes[i] = (byte) ((l >> p) & 0x0ff);
			p += 8;
		}
		return bytes;
	}

Um exemplo de execução com número positivo e saída ok:

	Long ourLong = 65000l;
	byte[] inBytes = Utils.longToByteArray(ourLong, 2);
	Long ourLongRecovered = Utils.byteArrayToLong(inBytes, null, true);
	System.out.println("ourLong: "+ourLong+" ourLongRecovered: "+ourLongRecovered);
// ourLong: 65000 ourLongRecovered: 65000

Agora com número negativo que dá erro:

		Long ourLong = -65000l;
		byte[] inBytes = Utils.longToByteArray(ourLong, 2);
		Long ourLongRecovered = Utils.byteArrayToLong(inBytes, null, true);
		System.out.println("ourLong: "+ourLong+" ourLongRecovered: "+ourLongRecovered);
// ourLong: -65000 ourLongRecovered: 536

pessoal, apenas adicionando aqui, não é 650001 não, é 65000L (no final é L, para transformar em Long), só pra ficar mais visível pra não confundir.

O bit de sinal corresponderá ao bit mais significativo do seu long. Portanto, estará no 8 byte. Você está serializando apenas alguns bytes, portanto, não o bit de sinal, ele não entra na conta. 65.000 é serializado corretamente pois cabe num unsigned short (é menor que 2^16-1, que é 65535).

Lembre-se também que números negativos trabalham por complemento de 2.

O número -65.000 é representado em binário por:
1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 0000 0010 0001 1000

Se você quiser um formato mais compacto, terá que alterar a forma como armazena longs.

Aproveitei que estava revisando seus métodos e os simplifiquei. Ginásticas com bits já são suficientemente complicadas, portanto, tente deixar as operações o mais explícitas e simples possíveis:

   public static long byteArrayToLong(byte[] b){
      return byteArrayToLong(b, b.length);
   }

   /** Converts from byte array to long.
   * @param b - byte array
   * @param size - the length
   * @return
   */
   public static long byteArrayToLong(byte[] b, int size){
      long ourLong = 0;
      for (int i = 0; i &lt; size; i++){
         ourLong = (ourLong &lt;&lt; 8) + (b[i] & 0xFF);
      }
      return ourLong;
   }

   public static byte[] longToByteArray(long number, int size){
      byte[] bytes = new byte[size];
      for (int i = 0; i &lt; size; i++){
         bytes[size-i-1] = (byte)(number & 0xFF);
         number &gt;&gt;= 8;
      }
      return bytes;
   }
}

Para funcionar com os negativos, use 8 no tamanho.
Também simplifiquei a assinatura das duas funções, evitando passar o null mágico. Esse estilo, com mais parâmetros e menos funções, é adotado pela MS. Eu particularmente sigo as convenções do java quando estou programando em Java (mais funções, menos parâmetros de flags).

Vini, valeuzão pela força, rodei aqui e foi, mas como vc disse eu tive que transformar o em um array de 8 bytes, no caso na aplicação que estou rodando esse valor é super variável, pode ser um array de 1,2,3…15, sendo que o tamanho que vou precisar para armazenar o long eu informo no código, na chamada do método… ai eu fiquei só com esse problema…

Eu vou agora mexer no método para ele executar como se fosse de 8 e no final tentar pegar apenas o valor que informo por parâmetro que é variável (ex. se for 2 retornar bytes[0] e bytes[1]), vou fazer aqui pra tentar, se tiver mais alguma dica quanto a isso pode passar que é bem vinda, to quebrando a cabeça desde cedo com esses bitwise/shift :O. Um forte abraço.

E como vc sabe que os 2 bytes armazenados representam um long e não um short?

Deixa eu tentar entender, pra o compilador o tamanho de um Long e um Short é apenas o tamanho de memória que eles ocupam, certo? então o valor 65000 em binário seria:
0000000000000000000000000000000000000000000000000111110101101000

Mas se eu por exemplo desse o valor:
0111110101101000

poderia também dizer que são 65000? certo?

esse mesmo valor só que negativo (-65000) seria a inversão do primeiro valor onde todos os 0 viram 1 e todos os 1 viram 0 ? ficaria:
1111 1111 1111 1111 0000 0010 0001 1000 (em 4bytes)

então o valor “0000 0010 0001 1000” não já seria o negativo de 65000 em 2 bytes?

Oi gente, como vai?

Apenas curiosidade, para que eu iria querer usar um desses dois métodos?

Abraço,
André AS

É isso mesmo tiagomac.

Uma forma comum de “compressão” de tipos inteiros sem sinal é a seguinte:

Coloque o byte menos significativo.
Se seu bit mais significativo for 1, passe para o próximo byte.
Repita o processo até que o bit mais significativo seja 0, ou o tamanho máximo do maior tipo numérico seja atingido.

Nesse caso, todos os dados são gravados e lidos como unsigned int (ou long, no seu caso), embora sua representação no protocolo possa ser menor de acordo com seu valor.

Para campos com sinal, pode-se usar um bit em algum lugar para representar o sinal, e o número sem sinal ser transmitido usando a compactação acima.

Ou André. Esses métodos permitem que você grave somente a quantidade necessária de bytes para representar um determinado valor numérico. Isso otimiza canais de rede, ou pode reduzir o tamanho de arquivos. É especialmente importante caso seu arquivo tenha muitos valores.

[quote=andredecotia]Oi gente, como vai?

Apenas curiosidade, para que eu iria querer usar um desses dois métodos?

Abraço,
André AS[/quote]

Pra um protocolo de comunicação por exemplo…