[JAVA] Arithmétique binaire de valeur binaire avec BigInteger

Puisque Java n'a pas beaucoup de fonctions pratiques pour gérer les valeurs binaires, afin d'effectuer des opérations sur les bits sur un tableau d'octets dans lequel les valeurs binaires sont stockées, le processus de passage de valeur entre les bits (tableau) est implémenté par lui-même. besoin de le faire.

Une méthode couramment utilisée consiste à stocker une fois le tableau d'octets dans un type entier tel que le type int ou le type long, d'effectuer une opération sur les bits, puis de le renvoyer dans le tableau d'octets. Avec cette méthode, la longueur maximale est de 8 octets. Ne peut gérer que jusqu'à.

En utilisant la classe BigInteger, vous pouvez gérer des tableaux d'octets de n'importe quelle taille, mais la conversion de données J'ai besoin de réfléchir un peu, alors j'ai résumé les points.

Comment effectuer une opération sur bits d'un tableau d'octets avec BigInteger

L'opération de bits du tableau d'octets à l'aide de BigInteger peut être effectuée par la procédure suivante.

  1. Lisez le tableau d'octets sous forme de nombre positif dans BigInteger.
  1. Effectuez diverses opérations à l'aide des méthodes de l'instance BigInteger.
  1. Convertissez à nouveau BigInteger en tableau d'octets.

Exemple d'implémentation

Point 1: Gardez BigInteger toujours positif

Si la valeur de BigInteger devient négative, cela peut ne pas fonctionner.

Substitution de décalage arithmétique pour décalage logique

Seuls les décalages arithmétiques sont implémentés dans BigInteger. (Parce que BigInteger est une classe pour gérer des valeurs numériques sans être conscient des chaînes d'octets, le décalage logique n'a aucune signification.) Par conséquent, il est nécessaire de remplacer le décalage logique par le décalage arithmétique, mais le décalage vers la droite pour les nombres négatifs est Les résultats diffèrent entre les décalages arithmétiques et les décalages logiques.

1010 1010 --Décalage droit logique 1 bit--> 0101 0101
1010 1010 --Décalage arithmétique à droite de 1 bit--> 1101 0101 #Le résultat est différent du décalage logique.

Par conséquent, il est nécessaire de convertir la valeur de BigInteger afin qu'elle soit toujours un nombre positif. Ce processus de conversion doit être effectué à chaque fois après la première lecture du tableau d'octets et chaque opération.

1010 1010
  --Convertir en BigInteger en positif--> 0000 1010 1010
  --Décalage arithmétique à droite de 1 bit--> 0000 0101 0101 #Le résultat est le même que le décalage logique.

Il existe deux façons de faire de BigInteger un nombre positif, l'une consiste à spécifier le bit de code et à lire le tableau d'octets, et l'autre à changer le bit de code à 1 par traitement de masque. Traitement de masque qui peut également être utilisé aux fins du point 2 décrit plus loin C'est facile à faire.

//Tableau d'octets d'entrée: c0 00
byte[] input = new Bytes(new byte[] {(byte) 0xc0, (byte) 0x00}).toByteArray();

//Comment lire comme un nombre positif (le bit de signe est 0)
// 00 c0 00
new BigInteger(1, input).toByteArray();

//Comment changer le bit de code à 0 en masquant
// ff c0 00 (contribution) AND 00 ff ff (masque) => 00 c0 00
//* Dans l'opération AND de BigInteger, qui a une taille différente lorsqu'il est transformé en tableau d'octets,
//Le plus petit est élargi pour s'adapter au plus grand.
BigInteger mask = new BigInteger(1, new Bytes(new byte[] {(byte) 0xff, (byte) 0xff}).toByteArray());
new BigInteger(input).and(mask).toByteArray();

Point 2: Gardez chaque bit de l'octet extra haut à 0

Lorsque l'incrémentation / décrémentation est effectuée sur une instance BigInteger et que la plage qui peut être exprimée par le nombre d'octets d'origine est dépassée, ou lorsqu'une opération de décalage vers la gauche est effectuée, les données sont affichées en octets supérieurs au nombre d'octets d'origine. Cela peut rester.

Etant donné que ces données d'octet d'ordre supérieur supplémentaires affectent l'opération de décalage dans le traitement suivant, il est nécessaire d'effacer chaque bit à 0 après chaque opération. Cela peut être fait en même temps que le processus de masquage décrit au point 1.

//Tableau d'octets d'entrée: c0 00
byte[] input = new Bytes(new byte[] {(byte) 0xc0, (byte) 0x00}).toByteArray();
BigInteger mask = new BigInteger(1, new Bytes(new byte[] {(byte) 0xff, (byte) 0xff}).toByteArray());
//Le calcul du décalage est effectué dans l'ordre du décalage à gauche de 1 bit et du décalage à droite de 1 bit.
//À l'origine, le bit 1 le plus significatif est rejeté et devient 80 00, mais il revient à l'original c 0 00.
new BigInteger(input).and(mask).shiftLeft(1).shiftRight(1);

Point 3: Ajustez le nombre d'octets lors du retour de BigInteger dans un tableau d'octets

La méthode toByteArray est requise pour représenter les nombres stockés dans BigInteger. Étant donné que le tableau d'octets avec le nombre minimal d'éléments est renvoyé, le tableau d'octets peut être plus petit que le tableau d'octets d'origine.

//Tableau d'octets d'entrée: 00 7f
byte[] input = new Bytes(new byte[] {(byte) 0x00, (byte) 0x7f}).toByteArray();
//Tableau d'octets de sortie: 7f
//Converti en BigInteger, il devient 1 octet. (Parce que 127 peut être représenté par 1 octet sous forme de nombre.)
byte[] output = new BigInteger(input).toByteArray();

De plus, chaque opération peut aboutir à un tableau d'octets d'une taille supérieure à la matrice d'octets d'origine.

//Tableau d'octets d'entrée: 40 00
byte[] input = new Bytes(new byte[] {(byte) 0x40, (byte) 0x00}).toByteArray();
//Tableau d'octets de sortie après décalage arithmétique gauche de 1 bit: 00 80 00
byte[] output = new BigInteger(input).shiftLeft(2).toByteArray();

Par conséquent, lors du retour au tableau d'octets, il est nécessaire d'ajuster pour qu'il corresponde au nombre d'octets d'origine.

//Instance BigInteger à convertir
BigInteger bi;
//Taille du tableau d'octets à convertir
int byteSize;

byte[] ba = bi.toByteArray();
ByteBuffer bb = ByteBuffer.allocate(byteSize);
//Supprimez les octets de poids très élevé.
if (ba.length >= byteSize) {
  bb.put(ba, ba.length-byteSize, byteSize);
//Remplissez le ByteBuffer depuis le début pour le nombre d'octets manquants.
} else {
  int byteSizeToFill = byteSize - ba.length;
  for(int i=0; i<byteSizeToFill; i++) {
    bb.put((byte) 0);
  }
  bb.put(ba);
}

//Tableau d'octets après conversion
byte[] output = bb.array();

Strictement parlant, si BigInteger est un nombre négatif, il faut remplir les octets manquants avec 1 au lieu de 0, mais puisque la valeur de BigInteger est ajustée pour que ce soit toujours un nombre positif en correspondance avec le point 1, le cas d'un nombre négatif Pas besoin de considérer.

//Tableau d'octets d'entrée: ff ff
byte[] input = new Bytes(new byte[] {(byte) 0xff, (byte) 0xff}).toByteArray();
//Tableau d'octets de sortie: ff
//Dans le cas d'un nombre négatif, il est nécessaire de remplir les bits de poids fort manquants avec 1.
byte[] output = new BigInteger(input).toByteArray();

Recommended Posts

Arithmétique binaire de valeur binaire avec BigInteger
Spécifiez la valeur par défaut avec @Builder of Lombok
Valeur de réglage de log4jdbc.properties
Vérifier le fonctionnement de deux rôles avec une application de chat