Précédent   Forum des professionnels en informatique > Java > Général Java
Général Java Java SE, Java ME, APIs, Persistance, JDBC, Spring, XML. Avant de poster -> FAQ Java, Sources Java
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 30/01/2012, 20h21   #1
Candidat au titre de Membre du Club
 
Inscription : décembre 2009
Messages : 27
Détails du profil
Informations forums :
Inscription : décembre 2009
Messages : 27
Points : 12
Points : 12
Par défaut Java et les types signés, ça marche comment?

Bonsoir,

Dans le cadre d'un projet, je dois réalisé une appli Java qui communique avec plusieurs matériels via JNI. Du coup, j'ai besoins de travailler en unsigned pour pas mal de chose. La plupart du temps, j'utilise le type "byte" (8bits signé). Mais j'ai un problème...

Code :
1
2
3
4
5
6
7
8
9
byte test = -1;
System.out.println(Integer.toBinaryString(test));
 
//11111111111111111111111111111111
 
test = 125;
System.out.println(Integer.toBinaryString(test));
 
//1111101
Donc, si je me souvient bien de mes cours d'infos, mon byte Java est sur 8 bits. Il est signé, donc le dernier bit est utilisé pour le signe. Du coup, on se retrouve avec une valeur max de 2⁷-1 (127).

Quelqu'un peut m'expliquer pourquoi la valeur de "-1" tient sur 32 bits??? J'avoue que je comprends pas car je m'attendrais à une valeur de "11111111" (sur 8 bits).

Merci d'avance
gagou7 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 30/01/2012, 20h31   #2
Modérateur
 
Avatar de tchize_
 
Homme
Responsable de service informatique
Inscription : avril 2007
Messages : 16 197
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : Belgique

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Service public

Informations forums :
Inscription : avril 2007
Messages : 16 197
Points : 25 343
Points : 25 343
Envoyer un message via MSN à tchize_ Envoyer un message via Skype™ à tchize_
Integer.toBinaryString convertit un int, pas un byte, ton byte de -1 est donc promo d'abord en integer, donc étendu à 32 bits.

Utilise Integer.toBinaryString(byte & 0xFF) pour n'avoir que les 8 bits de base.
__________________
⥀⥁ Чиз faq java, cours java, javadoc. Pensez à et
"Votre génitrice tute des pédoncules au pandémonium" (le conjurateur, 1973)
tchize_ est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 30/01/2012, 23h37   #3
Expert Confirmé Sénior
 
Inscription : septembre 2004
Messages : 5 092
Détails du profil
Informations forums :
Inscription : septembre 2004
Messages : 5 092
Points : 7 020
Points : 7 020
Citation:
Envoyé par gagou7 Voir le message
Quelqu'un peut m'expliquer pourquoi la valeur de "-1" tient sur 32 bits??? J'avoue que je comprends pas car je m'attendrais à une valeur de "11111111" (sur 8 bits).
Tout ce qui tient sur 8 bits tient aussi sur 32 bits, de même que 64 bits, 128 bits, et aussi 9 bits, 10 bits... .

-1 est une valeur de byte parfaitement correcte, et donc tient en effet sur 8 bits. Seulement, Integer.toBinaryString() ne traite que des ints, et les ints occupent 32 bits.
thelvin est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/01/2012, 08h40   #4
Candidat au titre de Membre du Club
 
Inscription : décembre 2009
Messages : 27
Détails du profil
Informations forums :
Inscription : décembre 2009
Messages : 27
Points : 12
Points : 12
Merci pour l'explication, j'avais zappé la conversion en integer...

J'ai toutefois une autre question: J'essaie d'utiliser des unsigned integer, du coup, mon raisonnement est le suivant.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
//Je lis des données équivalent à un unsigned int (32 bits), dans l'exemple je l'ai codé en dur
 
byte[] bytes = {(byte) 0xE0,(byte) 0x3A,(byte) 0xEC,0x02};
 
// Mes bytes sont en big-endian, du coup, pour récupérer la valeur, je fais comme ça:
 
int value = (((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) |
				  ((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff));
 
//Jusque là tout ce passe bien. Mais ma variable "value" étant un integer (32 bits) signé, si je tente d'afficher la valeur, j'obtiens un nombre négatif (normal vu que mon nombre est grand et que le MSB est à 1).
 
System.out.println(value); // -533009406 (en binaire: 11100000001110101110110000000010)
Mon idée est de convertir mon "value" dans un type plus grand ("long" de 64bits) et d'enlever les bits mis à 1 qui font de lui un nombre négatif.

En gros, mon "value" vaut : 11100000001110101110110000000010
Une fois passer en "long", il devrait valoir: 11111111111111111111111111111111 11100000001110101110110000000010
Je devrais pouvoir extraire la valeur positive grâce à "&0xFFFFFFFF".

Code :
1
2
 
System.out.println(((long)value)&0xFFFFFFFF); //Renvoie toujours un nombre négatif...
Pourquoi est-ce que j'ai toujours un nombre négatif? La fonction d'affichage (System.out.println()) supporte les long et ne fait pas de conversion implicite en integer pourtant... Je devrais obtenir 3761957890, non?

Merci encore une fois pour votre aide
gagou7 est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/01/2012, 09h22   #5
Membre habitué
 
Homme Gilles
Inscription : août 2011
Messages : 46
Détails du profil
Informations personnelles :
Nom : Homme Gilles
Localisation : France, Isère (Rhône Alpes)

Informations forums :
Inscription : août 2011
Messages : 46
Points : 118
Points : 118
Citation:
Envoyé par gagou7 Voir le message
Merci pour l'explication, j'avais zappé la conversion en integer...

J'ai toutefois une autre question: J'essaie d'utiliser des unsigned integer, du coup, mon raisonnement est le suivant.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
//Je lis des données équivalent à un unsigned int (32 bits), dans l'exemple je l'ai codé en dur
 
byte[] bytes = {(byte) 0xE0,(byte) 0x3A,(byte) 0xEC,0x02};
 
// Mes bytes sont en big-endian, du coup, pour récupérer la valeur, je fais comme ça:
 
int value = (((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) |
				  ((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff));
 
//Jusque là tout ce passe bien. Mais ma variable "value" étant un integer (32 bits) signé, si je tente d'afficher la valeur, j'obtiens un nombre négatif (normal vu que mon nombre est grand et que le MSB est à 1).
 
System.out.println(value); // -533009406 (en binaire: 11100000001110101110110000000010)
Mon idée est de convertir mon "value" dans un type plus grand ("long" de 64bits) et d'enlever les bits mis à 1 qui font de lui un nombre négatif.

En gros, mon "value" vaut : 11100000001110101110110000000010
Une fois passer en "long", il devrait valoir: 11111111111111111111111111111111 11100000001110101110110000000010
Je devrais pouvoir extraire la valeur positive grâce à "&0xFFFFFFFF".

Code :
1
2
 
System.out.println(((long)value)&0xFFFFFFFF); //Renvoie toujours un nombre négatif...
Pourquoi est-ce que j'ai toujours un nombre négatif? La fonction d'affichage (System.out.println()) supporte les long et ne fait pas de conversion implicite en integer pourtant... Je devrais obtenir 3761957890, non?

Merci encore une fois pour votre aide
En faisant un et logique comme celui là tu ne mets jamais à 0 le bit de signe.

Edit : faut que je compte les F D'abord ><

Edit2 : pourquoi tu n'affectes pas directement à un long ?
GPPro est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/01/2012, 09h49   #6
Candidat au titre de Membre du Club
 
Inscription : décembre 2009
Messages : 27
Détails du profil
Informations forums :
Inscription : décembre 2009
Messages : 27
Points : 12
Points : 12
Citation:
Envoyé par GPPro Voir le message
En faisant un et logique comme celui là tu ne mets jamais à 0 le bit de signe.

Edit : faut que je compte les F D'abord ><

Edit2 : pourquoi tu n'affectes pas directement à un long ?
Si je l'assigne directement en "long", ça ne change rien...

En faite j'ai trouvé C'est que Java quand il voit un 0xffffffff, il le cast en integer par défaut, même si on fait "var&(long)0xffffffff", le cast aura lieu et le 0xffffffff vaudra toujours "-1". La solution c'est de lui dire que notre nombre est par défaut un "long", ce qui nous donne 0xffffffffL. Du coup, ça marche nickel maintenant:

Code :
1
2
3
4
5
6
byte[] bytes = {(byte) 0xE0,(byte) 0x3A,(byte) 0xEC,0x02};
 
int value = (((bytes[0] & 0xff) << 24) | ((bytes[1] & 0xff) << 16) |
				  ((bytes[2] & 0xff) << 8) | (bytes[3] & 0xff));
 
System.out.println(value&0xffffffffL);
Merci encore !
gagou7 est déconnecté   Envoyer un message privé Réponse avec citation 10
Vieux 31/01/2012, 10h07   #7
Expert Confirmé Sénior
 
Inscription : septembre 2004
Messages : 5 092
Détails du profil
Informations forums :
Inscription : septembre 2004
Messages : 5 092
Points : 7 020
Points : 7 020
Citation:
Envoyé par gagou7 Voir le message
En faite j'ai trouvé C'est que Java quand il voit un 0xffffffff, il le cast en integer par défaut, même si on fait "var&(long)0xffffffff", le cast aura lieu et le 0xffffffff vaudra toujours "-1". La solution c'est de lui dire que notre nombre est par défaut un "long", ce qui nous donne 0xffffffffL.
La cause et la résolution sont bonnes. Mais l'explication est incorrecte : Java ne cast rien vers int.

0xffffffff est un int, dès le départ. Un int qui vaut -1.
Lorsque Java voit une expression qui mêle des ints et des longs, comme
Avec value de type long, et 0xffffffff de type intrinsèque int, Java décide que l'expression est de type le plus large : long.
Il commence donc par élargir les types ints vers le type long. Donc il élargit 0xffffffff vers le type long. Valeurs en tant que int : -1. Donc valeur en tant que long : -1. Donc après conversion en long, il n'y a toujours que des 1, et donc appliquer un & dessus n'a aucun effet.

En utilisant 0xffffffffL, c'est un long dès le départ, et c'est lui qui fait en sorte que l'expression soit de type long. Il n'a pas besoin d'être converti, et va effectivement faire passer tous les 32 bits de poids fort à zéro.
thelvin est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 31/01/2012, 12h02   #8
Modérateur
 
Avatar de tchize_
 
Homme
Responsable de service informatique
Inscription : avril 2007
Messages : 16 197
Détails du profil
Informations personnelles :
Sexe : Homme
Âge : 32
Localisation : Belgique

Informations professionnelles :
Activité : Responsable de service informatique
Secteur : Service public

Informations forums :
Inscription : avril 2007
Messages : 16 197
Points : 25 343
Points : 25 343
Envoyer un message via MSN à tchize_ Envoyer un message via Skype™ à tchize_
La question étant plutot, a part l'affichage qui vous gène (et peux donc être corrigé à l'affichage), pourquoi avoir besoin de "unsigned"?
__________________
⥀⥁ Чиз faq java, cours java, javadoc. Pensez à et
"Votre génitrice tute des pédoncules au pandémonium" (le conjurateur, 1973)
tchize_ est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 03/02/2012, 11h38   #9
Candidat au titre de Membre du Club
 
Inscription : décembre 2009
Messages : 27
Détails du profil
Informations forums :
Inscription : décembre 2009
Messages : 27
Points : 12
Points : 12
Citation:
Envoyé par tchize_ Voir le message
La question étant plutot, a part l'affichage qui vous gène (et peux donc être corrigé à l'affichage), pourquoi avoir besoin de "unsigned"?
Très bonne question C'est parce que mon appli Java récupère des valeurs "raw" sous forme de byte en provenance de différent périphérique (CAN, RS232 et SPI) grâce à JNI. Je dois traiter ces byte pour les reconvertir en double, float, int ou short.

Du coup, j'ai besoins de bien comprendre comment fonctionne Java au niveau des différents types qu'il offre. Dans le cas présenté ici, c'est uniquement pour avoir le bon affichage comme si c'était un unsigned (même si en faite on place la valeur dans une variable plus grande pour que le bit de signe ne soit pas interprété).

On pourrait croire que signed ou unsigned, peut importe car au niveau binaire ça ne change rien, mais c'est faux ! Le faite que le type unsigned n'existe pas en Java pose des problèmes lorsqu'on passe d'un type à un autre avec des variables dont le MSB est à 1. En effet, pour que la valeur soit toujours négative, des "1" sont rajoutés dans certains cas. Du coup, lorsque l'on s'amuse avec des masques et des shift et qu'on ne connait pas forcément comment Java fonctionne, on se retrouve avec des "castings implicite" en "int" qui rajoute des "1" et qui peuvent perturber nos calcules.

Le problème est aussi présent lorsque l'on shift à droite si on utilise pas le bonne opérateur (">>>" au lieu de ">>").

Entre temps et en faisant pas mal de test, je suis tombé sur ce site qui explique comment fonctionne Java avec les opérateurs de "shiftage".

Dans mon cas précis, l'appli devra tourner sur un système embarqué peu puissant. Et je dois convertir des tableaux de byte (big-endian ou little-endian) en d'autres types. C'est pour ça que je n'utilise pas de ByteBuffer par exemple, car après avoir fait un petit benchmark, il s'était avérer qu'un ByteBuffer étant 100x plus lent qu'en faisant le traitement à la main (ET/OU logique, masque et shift).

Bref, c'est des choses à savoir (que je ne savais pas) si on veut traiter/modifier des valeurs "raw" pour les rendre "utilisable" par la suite.

En tout cas merci beaucoup de votre aide, j'ai acquis des connaissances qui me sont très utiles
gagou7 est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 07h04.


 
 
 
 
Partenaires

Hébergement Web