Pouvons-nous créer un octet non signé en Java

189

J'essaie de convertir un octet signé en non signé. Le problème est que les données que je reçois ne sont pas signées et que Java ne prend pas en charge l'octet non signé, donc quand il lit les données, il les traite comme signées.

J'ai essayé de le convertir par la solution suivante que j'ai obtenue de Stack Overflow.

public static int unsignedToBytes(byte a)
{
    int b = a & 0xFF;
    return b;
}

Mais quand il est à nouveau converti en octet, j'obtiens les mêmes données signées. J'essaie d'utiliser ces données comme paramètre d'une fonction de Java qui n'accepte qu'un octet comme paramètre, je ne peux donc pas utiliser d'autre type de données. Comment puis-je résoudre ce problème?

dln
la source
2
Guava: UnsignedBytes.toint (valeur d'octet)
jacktrades
21
java.lang.Byte.toUnsignedInt (valeur d'octet);
themarketka

Réponses:

108

Je ne suis pas sûr de comprendre votre question.

Je viens d'essayer ceci et pour l'octet -12 (valeur signée), il a renvoyé l'entier 244 (équivalent à la valeur d'octet non signé mais tapé comme un int):

  public static int unsignedToBytes(byte b) {
    return b & 0xFF;
  }

  public static void main(String[] args) {
    System.out.println(unsignedToBytes((byte) -12));
  }

Est-ce ce que tu veux faire?

Java ne permet pas d'exprimer 244 sous forme de bytevaleur, comme le ferait C. Pour exprimer des entiers positifs au-dessus de Byte.MAX_VALUE(127), vous devez utiliser un type d'intégrale différent, comme short, intou long.

Guillaume
la source
1
byte b = (byte)unsignedToBytes((byte) -12); essayez maintenant d'imprimer b
Jigar Joshi
102
Pourquoi avez-vous accepté cela comme la bonne réponse? Tout ce qu'il fait est exactement la même que la méthode que vous mentionnez dans votre question - convertir un octet en un entier non signé.
Adamski
1
Il est important d'avoir parfois des valeurs signées, parfois non signées, c'est probablement la raison pour laquelle il a accepté cette réponse. (byte) (b & 0xff) n'a aucun sens, mais (byte) (Math.min ((b & 0xff) * 2, 255)) a un sens, par exemple en infographie, cela rendra simplement le pixed représenté par l'octet deux fois plus clair. :-)
iirekm
3
Il pourrait être appelé byteToUnsigned aussi
Hernán Eche
197

Le fait que les primitives soient signées en Java n'a pas d'importance pour la façon dont elles sont représentées en mémoire / transit - un octet est simplement 8 bits et que vous l'interprétiez comme une plage signée ou non, c'est à vous de décider. Il n'y a pas de drapeau magique pour dire "ceci est signé" ou "ceci n'est pas signé".

Lorsque les primitives sont signées, le compilateur Java vous empêchera d'attribuer une valeur supérieure à +127 à un octet (ou inférieure à -128). Cependant, rien ne vous empêche de baisser un int (ou un court) pour y parvenir:

int i = 200; // 0000 0000 0000 0000 0000 0000 1100 1000 (200)
byte b = (byte) 200; // 1100 1000 (-56 by Java specification, 200 by convention)

/*
 * Will print a negative int -56 because upcasting byte to int does
 * so called "sign extension" which yields those bits:
 * 1111 1111 1111 1111 1111 1111 1100 1000 (-56)
 *
 * But you could still choose to interpret this as +200.
 */
System.out.println(b); // "-56"

/*
 * Will print a positive int 200 because bitwise AND with 0xFF will
 * zero all the 24 most significant bits that:
 * a) were added during upcasting to int which took place silently
 *    just before evaluating the bitwise AND operator.
 *    So the `b & 0xFF` is equivalent with `((int) b) & 0xFF`.
 * b) were set to 1s because of "sign extension" during the upcasting
 *
 * 1111 1111 1111 1111 1111 1111 1100 1000 (the int)
 * &
 * 0000 0000 0000 0000 0000 0000 1111 1111 (the 0xFF)
 * =======================================
 * 0000 0000 0000 0000 0000 0000 1100 1000 (200)
 */
System.out.println(b & 0xFF); // "200"

/*
 * You would typically do this *within* the method that expected an 
 * unsigned byte and the advantage is you apply `0xFF` only once
 * and than you use the `unsignedByte` variable in all your bitwise
 * operations.
 *
 * You could use any integer type longer than `byte` for the `unsignedByte` variable,
 * i.e. `short`, `int`, `long` and even `char`, but during bitwise operations
 * it would get casted to `int` anyway.
 */
void printUnsignedByte(byte b) {
    int unsignedByte = b & 0xFF;
    System.out.println(unsignedByte); // "200"
}
Adamski
la source
5
Pour de nombreuses opérations, il ne fait aucune différence, mais pour certaines opérations, il le fait. Dans les deux cas, vous pouvez utiliser un octet comme non signé ou utiliser char qui n'est pas signé.
Peter Lawrey
63
L'accès à un tableau avec un nombre potentiellement négatif n'est pas sans importance.
Stefan
3
@Stefan - Je voulais dire non pertinent dans le contexte de la façon dont ils sont représentés sur le fil.
Adamski
6
Ce qui est quelque peu sans rapport avec la question. Puisqu'il a mentionné qu'il a besoin de le transmettre à une fonction qui n'accepte que les paramètres d'octets, peu importe la météo, nous l'interprétons comme la représentation octet d'une licorne. Java le traitera toujours comme un nombre signé, ce qui peut être problématique pour un exemple lorsque cette fonction utilise le paramètre comme un index. Cependant, pour être juste, j'ai également décliné les 2 autres réponses principales, car elles ne répondent pas non plus à la question.
Stefan
2
@Stefan +1 pour vous. Absolument pertinent si vous utilisez l'octet pour accéder à un tableau de 256 éléments. C'est un excellent exemple pour démontrer pourquoi tout le monde devrait commencer à apprendre C et C ++ avant de passer à Java ou C #
Gianluca Ghettini
46

Guide complet pour travailler avec des octets non signés en Java:

Octet non signé en Java

(Source de cette réponse.)


Le langage Java ne fournit rien de tel que le unsignedmot - clé. A byteselon la spécification de la langue représente une valeur comprise entre -128 - 127. Par exemple, si un byteest castée intJava interprétera le premier bit comme le signe et l' utilisation extension de signe .

Cela étant dit, rien ne vous empêche de visualiser bytesimplement 8 bits et d'interpréter ces bits comme une valeur comprise entre 0 et 255. Gardez simplement à l'esprit que vous ne pouvez rien faire pour forcer votre interprétation sur la méthode de quelqu'un d'autre. Si une méthode accepte a byte, alors cette méthode accepte une valeur entre −128 et 127 sauf indication contraire explicite.

Voici quelques conversions / manipulations utiles pour votre commodité:

Conversions vers / depuis int

// From int to unsigned byte
int i = 200;                    // some value between 0 and 255
byte b = (byte) i;              // 8 bits representing that value

// From unsigned byte to int
byte b = 123;                   // 8 bits representing a value between 0 and 255
int i = b & 0xFF;               // an int representing the same value

(Ou, si vous utilisez Java 8+, utilisez Byte.toUnsignedInt.)

Analyse / formatage

Le meilleur moyen est d'utiliser les conversions ci-dessus:

// Parse an unsigned byte
byte b = (byte) Integer.parseInt("200");

// Print an unsigned byte
System.out.println("Value of my unsigned byte: " + (b & 0xFF));

Arithmétique

La représentation à 2 compléments "fonctionne juste" pour l'addition, la soustraction et la multiplication:

// two unsigned bytes
byte b1 = (byte) 200;
byte b2 = (byte) 15;

byte sum  = (byte) (b1 + b2);  // 215
byte diff = (byte) (b1 - b2);  // 185
byte prod = (byte) (b2 * b2);  // 225

La division nécessite une conversion manuelle des opérandes:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));
aioobe
la source
36

Il n'y a pas d'octets primitifs non signés en Java. La chose habituelle est de le convertir en un type plus grand:

int anUnsignedByte = (int) aSignedByte & 0xff;
Peter Knego
la source
Le cast en int est-il nécessaire?
nich le
Cela peut être un casting implicite, mais il y a un casting dans les deux cas. Et cette distribution fait une extension signée. Et c'est un problème. Si vous effectuez un casting explicite, vous pouvez au moins voir que cela se produit.
foo
4

Une note latérale, si vous voulez l'imprimer, vous pouvez simplement dire

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));
Kyle Kinkade
la source
6
pourquoi si complexe? println(b & 0xff)est assez
phuclv
0

Bien que cela puisse sembler ennuyeux (venant de C) que Java n'inclue pas d'octet non signé dans le langage, ce n'est vraiment pas grave car une simple opération "b & 0xFF" donne la valeur non signée pour l'octet (signé) b dans le (rare) situations où il est réellement nécessaire. Les bits ne changent pas réellement - juste l'interprétation (ce qui n'est important que lorsque vous effectuez par exemple des opérations mathématiques sur les valeurs).

Bob
la source
regardez les autres répondre, vous pensez que votre réponse est la meilleure / utile? décrire en peu et l'ajouter dans les commentaires
Jubin Patel
9
Ce n'est pas rare simplement parce que vous ne l'avez pas rencontré. Essayez de mettre en œuvre un protocole et vous le rencontrerez un million de fois. Ce qui est ennuyeux, c'est que la grande majorité des cas d'utilisation que j'ai rencontrés traitent d'octets, vous voulez traiter des octets non signés (car ce sont des octets, pas des nombres). Le plus fou est que TOUTE opération au niveau du bit le convertira en un entier, ce qui signifie que toute valeur "négative" sera des valeurs complètement différentes une fois étendue. Oui, vous pouvez le contourner en masquant toujours, mais c'est une perte de temps, processeur, et provoque des bogues vraiment obscurs si vous oubliez.
Thor84no
Je suis d'accord avec Thor84no: les octets ne sont pas des nombres et ne devraient pas avoir de signe. D'un autre côté, comme ce ne sont pas des nombres, nous ne devrions même pas avoir / utiliser les opérateurs + et -. Utiliser uniquement des opérateurs au niveau du bit fonctionne très bien, de l'autre côté, les opérateurs de décalage ne fonctionnent pas comme on le souhaiterait, et java promeut en effet un octet décalé vers un int.
user1708042
1
@ VlastimilOvčáčík C'est littéralement impossible dans ce cas, c'est ce qui est agitant. Soit vous répétez x & 0xFFpartout où vous en avez besoin, soit vous répétez quelque chose comme behaveLikeAnUnsignedByte(x)partout. Ceci est nécessaire pour chaque endroit où vous utilisez une valeur d'octet ou un tableau d'octets qui doit être non signé, il n'y a aucun moyen concevable d'éviter cette répétition. Vous ne pouvez pas écrire l'implémentation d'un protocole qui lit et écrit des valeurs d'octet avec une seule référence à une variable d'octet. Votre vision simpliste pourrait expliquer pourquoi ils ne se sont jamais souciés de le réparer.
Thor84no
0

Si vous pensez que vous cherchez quelque chose comme ça.

public static char toUnsigned(byte b) {
    return (char) (b >= 0 ? b : 256 + b);
}
Tobias Johansson
la source
0

Adamski a fourni la meilleure réponse, mais elle n'est pas tout à fait complète, alors lisez sa réponse, car elle explique les détails que je ne suis pas.

Si vous avez une fonction système qui nécessite qu'un octet non signé lui soit transmis, vous pouvez transmettre un octet signé car il le traitera automatiquement comme un octet non signé.

Donc, si une fonction système nécessite quatre octets, par exemple, 192 168 0 1 en tant qu'octets non signés, vous pouvez passer -64-88 0 1, et la fonction fonctionnera toujours, car le fait de les passer à la fonction les dé-signera .

Cependant, il est peu probable que vous rencontriez ce problème car les fonctions système sont cachées derrière des classes pour une compatibilité multiplateforme, bien que certaines des méthodes de lecture java.io renvoient des octets non mesurés sous forme d'int.

Si vous voulez que cela fonctionne, essayez d'écrire des octets signés dans un fichier et de les relire en tant qu'octets non signés.

Huard de brebis
la source
1
Il n'y a pas d'octets signés ou non signés.
Vlastimil Ovčáčík
Comment avez-vous exactement écrit et lu les octets de votre exemple?
Vlastimil Ovčáčík
0

Vous pouvez aussi:

public static int unsignedToBytes(byte a)
{
    return (int) ( ( a << 24) >>> 24);
}    

Explication:

Disons a = (byte) 133;

En mémoire, il est stocké sous le nom: "1000 0101" (0x85 en hexadécimal)

Donc sa représentation traduit unsigned = 133, signé = -123 (comme complément à 2)

a << 24

Lorsque le décalage gauche est effectué de 24 bits vers la gauche, le résultat est maintenant un entier de 4 octets qui est représenté par:

"10000101 00000000 00000000 00000000" (ou "0x85000000" en hexadécimal)

ensuite nous avons

(a << 24) >>> 24

et il décale à nouveau sur les 24 bits de droite mais se remplit de zéros non significatifs. Il en résulte donc:

"00000000 00000000 00000000 10000101" (ou "0x00000085" en hexadécimal)

et c'est la représentation non signée qui vaut 133.

Si vous essayez de lancer un cast, a = (int) a; ce qui se passerait, c'est qu'il conserve la représentation du complément à 2 de byte et le stocke sous la forme int également en tant que complément à 2:

(entier) "10000101" ---> "11111111 11111111 11111111 10000101"

Et cela se traduit par: -123

mark_infinite
la source
2
En 2019, ce n'est pas nécessaire. Utilisez juste java.lang.Byte.toUnsignedInt(byte value). Et si vous n'utilisez pas encore Java 8, mettez à niveau dès que possible. Java 7 et les versions antérieures sont en fin de vie.
Stephen C le
0

J'essaie d'utiliser ces données comme paramètre d'une fonction de Java qui n'accepte qu'un octet comme paramètre

Ce n'est pas sensiblement différent d'une fonction acceptant un entier auquel vous voulez passer une valeur supérieure à 2 ^ 32-1.

Cela semble dépendre de la façon dont la fonction est définie et documentée; Je vois trois possibilités:

  1. Il peut explicitement documenter que la fonction traite l'octet comme une valeur non signée, auquel cas la fonction devrait probablement faire ce que vous attendez mais semblerait être mal implémentée. Pour le cas des nombres entiers, la fonction déclarerait probablement le paramètre comme un entier non signé, mais ce n'est pas possible pour le cas des octets.

  2. Il peut documenter que la valeur de cet argument doit être supérieure (ou peut-être égale à) zéro, auquel cas vous utilisez mal la fonction (en passant un paramètre hors limites), en vous attendez à ce qu'elle fasse plus que ce pour quoi elle a été conçue faire. Avec un certain niveau de prise en charge du débogage, vous pouvez vous attendre à ce que la fonction lève une exception ou échoue une assertion.

  3. La documentation peut ne rien dire, auquel cas un paramètre négatif est, eh bien, un paramètre négatif et si cela a une signification dépend de ce que fait la fonction. Si cela n'a pas de sens, alors peut-être que la fonction devrait vraiment être définie / documentée comme (2). Si cela est significatif d'une manière non évidente (par exemple, des valeurs non négatives sont utilisées pour indexer dans un tableau, et des valeurs négatives sont utilisées pour indexer à partir de la fin du tableau donc -1 signifie le dernier élément), la documentation devrait dire ce qu'elle signifie et je m'attendrais à ce que ce ne soit pas ce que vous voulez qu'il fasse de toute façon.

Kevin Martin
la source
Hmmm, je pense que je viens de publier une réponse qui était destinée à une autre question sur la signature des octets, mais je suppose que c'est encore un peu pertinent ici aussi ...
Kevin Martin
-1

Il n'y a pas d'octet non signé en Java, mais si vous voulez afficher un octet, vous pouvez faire,

int myInt = 144;

byte myByte = (byte) myInt;

char myChar = (char) (myByte & 0xFF);

System.out.println("myChar :" + Integer.toHexString(myChar));

Production:

myChar : 90

Pour plus d'informations, veuillez vérifier, Comment afficher une valeur hexadécimale / octet en Java .

Jyo l'odeur
la source
Il n'est pas nécessaire de définir cela vous-même. java.lang.Byte.toUnsignedInt(byte value);existe pour cela.
Alexander - Réintègre Monica
-2

Si vous avez une fonction qui doit recevoir un octet signé, que voulez-vous qu'elle fasse si vous passez un octet non signé?

Pourquoi ne pouvez-vous utiliser aucun autre type de données?

Exceptionnellement, vous pouvez utiliser un octet comme un octet non signé avec des traductions simples ou sans traduction. Tout dépend de la manière dont il est utilisé. Vous auriez besoin de clarifier ce que vous voulez en faire.

Peter Lawrey
la source
-2

Oui et non. J'ai fouillé avec ce problème. Comme je comprends ceci:

Le fait est que java a signé des entiers -128 à 127 .. Il est possible de présenter un unsigned en java avec:

public static int toUnsignedInt(byte x) {
    return ((int) x) & 0xff;
}

Si, par exemple, vous ajoutez -12 numéro signé pour ne pas être signé, vous obtenez 244. Mais vous pouvez à nouveau utiliser ce numéro dans signé, il doit être redéposé à signé et il sera à nouveau -12.

Si vous essayez d'ajouter 244 à l'octet java, vous obtiendrez outOfIndexException.

À votre santé..

Sindri Þór
la source
3
Il n'est pas nécessaire de définir cela vous-même. java.lang.Byte.toUnsignedInt(byte value);existe pour cela.
Alexander - Réintègre Monica
-3

Conformément aux limitations de Java, l'octet non signé est presque impossible dans le format de type de données actuel. Vous pouvez choisir d'autres bibliothèques d'une autre langue pour ce que vous implémentez, puis vous pouvez les appeler en utilisant JNI .

Pritesh Jain
la source
Je ne pense pas qu'il veuille le stocker sous forme d'octet signé. Il le reçoit sous forme d'octet signé et souhaite le stocker sous forme d'int, ce qui est parfaitement valide. Son problème est que partout où il obtient des entrées, il représente une valeur entre 0 et 255 sous forme d'octet, mais Java interprète cela comme une valeur signée complémentaire à deux parce que java ne prend pas en charge les octets signés.
Zac
-3

Si vous voulez des octets non signés en Java, soustrayez simplement 256 du nombre qui vous intéresse. Cela produira un complément à deux avec une valeur négative, qui est le nombre souhaité en octets non signés.

Exemple:

int speed = 255; //Integer with the desired byte value
byte speed_unsigned = (byte)(speed-256);
//This will be represented in two's complement so its binary value will be 1111 1111
//which is the unsigned byte we desire.

Vous devez utiliser de tels hacks sales lorsque vous utilisez leJOS pour programmer la brique NXT .

XapaJIaMnu
la source
Vous réalisez que la valeur binaire de 255 est également 1111 1111, donc aucune soustraction n'est nécessaire, non?
Nick White
@NickWhite, oui en binaire. Mais java utilise le comlement de 2 où 255 n'est pas 11111111
XapaJIaMnu
Désolé, mais c'est tout simplement faux. Essayez quelques expériences. La valeur dans speed_unsignedest signée. Imprimez-le et voyez. (Et le - 256ne fait rien ici.)
Stephen C