Comment convertir un Int en octet non signé et inversement

92

J'ai besoin de convertir un nombre en octet non signé. Le nombre est toujours inférieur ou égal à 255, il tiendra donc dans un octet.

J'ai également besoin de reconvertir cet octet en ce nombre. Comment ferais-je cela en Java? J'ai essayé plusieurs façons et aucune ne fonctionne. Voici ce que j'essaye de faire maintenant:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

et maintenant pour reconvertir cet octet en nombre:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

De toute évidence, cela ne fonctionne pas. Pour une raison quelconque, il convertit toujours le nombre en 65. Aucune suggestion?

ciel sombre
la source
J'ai aussi essayé cette méthode: crazysquirrel.com/computing/java/basics / ... - ne fonctionne pas.
darksky le

Réponses:

201

Un octet est toujours signé en Java. Vous pouvez cependant obtenir sa valeur non signée en la complétant binaire avec 0xFF:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
la source
J'ai encore 65 ans ... Pourquoi ça?
sombre du
1
Il stocke le numéro que vous voulez dans l'octet. Java le considère simplement comme un nombre signé plutôt que comme un nombre non signé. Mais chaque bit est le même que s'il s'agissait d'un nombre non signé. Votre conversion de chaîne en octets est une autre question sans rapport. Postez-le séparément, dans une nouvelle question.
JB Nizet
1
utilisez getInt pour récupérer votre int et transformez l'int en octet. Un int est un int. D'où il vient n'a pas d'importance.
JB Nizet
19
Pourquoi bit à bit et avec 0xFFaboutissent-ils à un entier non signé? Une explication serait vraiment utile.
user462455
2
Java 8 utilise la même méthode: public static int toUnsignedInt (byte x) {return ((int) x) & 0xff; }
Daniel De León
55

Java 8 permet Byte.toUnsignedIntde convertir byteen intpar conversion non signée. Dans le JDK d'Oracle, cela est simplement implémenté return ((int) x) & 0xff;car HotSpot comprend déjà comment optimiser ce modèle, mais il pourrait être intrinsèque sur d'autres VM. Plus important encore, aucune connaissance préalable n'est nécessaire pour comprendre ce qu'un appel à toUnsignedInt(foo)faire.

Au total, Java 8 fournit des méthodes pour convertir byteet shorten non signé intet long, et inten non signé long. Une méthode de conversion byteen non signé a shortété délibérément omise car la JVM ne fournit que l'arithmétique sur intet de longtoute façon.

Pour convertir un retour int à un octet, il suffit d' utiliser un casting: (byte)someInt. La conversion primitive de rétrécissement résultante supprimera tous les bits sauf les 8 derniers.

Jeffrey Bosboom
la source
7

Si vous avez juste besoin de convertir une valeur 8 bits attendue d'un entier signé en une valeur non signée, vous pouvez utiliser un simple décalage de bits:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Si vous utilisez autre chose que intle type de base, vous devrez évidemment ajuster le montant du décalage: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

De plus, gardez à l'esprit que vous ne pouvez pas utiliser de bytetype, cela entraînera une valeur signée comme mentionné par d'autres répondants. Le plus petit type de primitif que vous pourriez utiliser pour représenter une valeur non signée 8 bits serait a short.

dm78
la source
3

L' Integer.toString(size)appel se convertit en la représentation char de votre entier, c'est-à-dire le char '5'. La représentation ASCII de ce caractère est la valeur 65.

Vous devez d'abord analyser la chaîne en une valeur entière, par exemple en utilisant Integer.parseInt, pour récupérer la valeur int originale.

En fin de compte, pour une conversion signée / non signée, il est préférable de ne Stringpas figurer dans l'image et d'utiliser la manipulation de bits comme le suggère @JB.

Péter Török
la source
Alors comme ça? (Encore 65 ans ici)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
darksky
@Nayefc, j'ai mis à jour ma réponse au moment où vous écriviez votre commentaire :-)
Péter Török
D'accord merci! Pour une raison quelconque, il en imprime ENCORE 65. Qu'est-ce que ça veut dire? Si elle montre juste 65, ce qui est réellement stocké dans l'octet, je ne l'obtiens pas. Veuillez également vérifier ma question, je l'ai modifiée et ajouté une section sur la conversion des chaînes :)
darksky
@Nayefc, encore une fois, String.getBytes()donne les valeurs ASCII des caractères contenus dans le texte, pas les valeurs numériques des chiffres. Vous devez analyser la chaîne en une valeur numérique, comme je l'ai indiqué ci-dessus.
Péter Török
1
@Nayefc, donc c'est en fait une question complètement différente. Et AFAIS ça devrait marcher. Créez une nouvelle question, y compris l'entrée et la sortie réelles de la conversion, ainsi que le code complet pour reproduire le cas.
Péter Török
3

La solution fonctionne bien (merci!), Mais si vous voulez éviter le cast et laisser le travail de bas niveau au JDK, vous pouvez utiliser un DataOutputStream pour écrire vos int et un DataInputStream pour les relire. Ils sont automatiquement traités comme non signés octets alors:

Pour convertir des entiers en octets binaires;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Les relire dans:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. utile pour gérer les formats de données binaires (par exemple, les formats de messages plats, etc.)

Gregor
la source
3

Sauf que chartous les autres types de données numériques en Java sont signés.

Comme indiqué dans une réponse précédente, vous pouvez obtenir la valeur non signée en effectuant une andopération avec 0xFF. Dans cette réponse, je vais vous expliquer comment cela se passe.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Si votre machine est 32 bits, le inttype de données a besoin de 32 bits pour stocker les valeurs. byten'a besoin que de 8 bits.

La intvariable iest représentée dans la mémoire comme suit (sous la forme d'un entier 32 bits).

0{24}11101010

Ensuite, la bytevariable best représentée par:

11101010

Comme bytes ne sont pas signés, cette valeur représente -22. (Recherchez le complément de 2 pour en savoir plus sur la façon de représenter des entiers négatifs en mémoire)

Ensuite, si vous lancez, intc'est toujours -22parce que le lancer préserve le signe d'un nombre.

1{24}11101010

La 32-bitvaleur castée de l' bexécution de l' andopération avec 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Ensuite, vous obtenez 234la réponse.

Ramesh-X
la source
1

Gestion des octets et des entiers non signés avec BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
pgrandjean
la source
1

Même s'il est trop tard, j'aimerais donner mon avis à ce sujet car cela pourrait clarifier pourquoi la solution proposée par JB Nizet fonctionne. Je suis tombé sur ce petit problème en travaillant sur un analyseur d'octets et à la conversion de chaînes moi-même. Lorsque vous copiez d'un type intégral de taille plus grande vers un type intégral de taille plus petite, comme ce document java dit que cela se produit:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Une conversion restrictive d'un entier signé en un type intégral T rejette simplement tout sauf le n le plus bas bits d'ordre, où n est le nombre de bits utilisés pour représenter le type T.En plus d'une éventuelle perte d'informations sur l'amplitude de la valeur numérique, cela peut faire en sorte que le signe de la valeur résultante diffère du signe de la valeur d'entrée .

Vous pouvez être sûr qu'un octet est un type intégral comme ce document java dit https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html octet: Le type de données octet est un deux signés 8 bits complément entier.

Donc, dans le cas de la conversion d'un entier (32 bits) en un octet (8 bits), vous copiez simplement le dernier (8 bits les moins significatifs) de cet entier dans la variable d'octet donnée.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

La deuxième partie de l'histoire concerne la manière dont les opérateurs unaires et binaires Java promeuvent les opérandes. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 L' élargissement de la conversion primitive (§5.1.2) est appliqué pour convertir l'un ou les deux opérandes comme spécifié par les règles suivantes:

Si l'un des opérandes est de type double, l'autre est converti en double.

Sinon, si l'un des opérandes est de type float, l'autre est converti en float.

Sinon, si l'un des opérandes est de type long, l'autre est converti en long.

Sinon, les deux opérandes sont convertis en type int.

Rassurez-vous, si vous travaillez avec le type intégral int et / ou inférieur, il sera promu en int.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

Je me suis aussi gratté la tête :). Il y a une bonne réponse ici par rgettman. Opérateurs binaires en java uniquement pour les entiers et les longs?

KaziQ
la source
0

Si vous souhaitez utiliser les classes wrapper primitives, cela fonctionnera, mais tous les types java sont signés par défaut.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Roméo
la source
-1

en java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

Résultat: 254

Tristan De Smet
la source