2010-09-30 5 views
1

Je veux lire 4 octets qui sont un codage little-endian d'un entier 32 bits non signé, et affecter la valeur à un Java intPeut-on assigner 4 octets ordonnés en little-endian d'un entier non-signé à une primitive Java en utilisant juste des opérateurs au niveau du bit?

(Oui, en réalité, je vais utiliser un «long», mais dans Dans ce cas, je sais que la valeur non signée n'est jamais si grande qu'elle débordera d'un int signé dans la notation du complément à deux, et il convient à mon illustration d'utiliser un int).

Les 4 octets en question encodent la valeur '216' de style little-endian:

0xD8000000 

Et au fond, j'ai juste besoin de bourrer le motif de bits suivant dans Java int:

0x000000D8 

Le suivant le code simple devrait le faire ... et pour les trois premiers '0x00' octets il réussit:

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; 

Cependant, il plisse sur:

s = s | b1; 

... car les bits de b1 sont 1101 1000, ce qui est un nombre négatif (-40) en deux la notation de complément à, depuis est un 1 le bit le plus significatif Lorsque Java étend b1 à un int avant d'évaluer le bit ou l'opérateur |, -40 est codé comme 0xFFFFFFD8, ce qui viole notre supposition naïve que les 3 premiers octets de l'int élargi seront des 0.

Donc, ma stratégie échoue. Mais que dois-je faire à la place? Est-il même possible de résoudre cela en utilisant des opérateurs primitifs (donnez une solution) ou devons-nous recourir à la bibliothèque de classes? (Je ne joue pas avec les bits et les octets directement dans mon codage normal, donc je n'ai pas l'idiome pour ce qui semble être un code 'de tous les jours').

En ce qui concerne l'approche de la bibliothèque de classes, le fragment suivant obtient le bon résultat:

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(); 

... ce qui est bien pour une meilleure lisibilité, mais utilise 8 invocations de méthode que je préfère passer.

merci! David.

Répondre

6

Il suffit d'inclure & 0xff pour chaque octet int promotion, afin de faire en sorte que les bits supérieurs sont ensemble à 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; 

Ou plus compacte:

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); 

(De toute évidence le "shift left 0" est inutile, mais il garde la cohérence plus haut.)

+1

Vous devez surveiller cette extension de signe. – dty

+0

Ah, j'avais vu ce genre de chose pendant ma chasse sans prendre le temps d'exécuter le code dans ma tête. Mais maintenant que vous le dites, c'est tout à fait logique et je ne serai pas intimidé par ce petit paquet concis à l'avenir! Merci. –

Questions connexes