Comportement étrange lorsque Java convertit int en octet?

130
int i =132;

byte b =(byte)i; System.out.println(b);

Époustouflant. Pourquoi la sortie -124?

dur
la source

Réponses:

172

En Java, an intvaut 32 bits. A byteest 8bits .

La plupart des types primitifs en Java sont signés, et byte, short, intet longsont codés en complément à deux. (Le chartype n'est pas signé et le concept de signe n'est pas applicable àboolean .)

Dans ce schéma numérique, le bit le plus significatif spécifie le signe du nombre. Si plus de bits sont nécessaires, le bit le plus significatif ("MSB") est simplement copié dans le nouveau MSB.

Donc, si vous avez byte 255: 11111111 et que vous voulez le représenter comme un int(32 bits), vous copiez simplement le 1 vers la gauche 24 fois.

Maintenant, une façon de lire un nombre de complément à deux négatifs est de commencer par le bit le moins significatif, de se déplacer vers la gauche jusqu'à ce que vous trouviez le premier 1, puis d'inverser chaque bit par la suite. Le nombre résultant est la version positive de ce nombre

Par exemple: 11111111va à 00000001=-1 . C'est ce que Java affichera comme valeur.

Ce que vous voulez probablement faire est de connaître la valeur non signée de l'octet.

Vous pouvez accomplir cela avec un masque de bits qui supprime tout sauf les 8 bits les moins significatifs. (0xff)

Alors:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

Imprimera: "Signed: -1 Unsigned: 255"

Que se passe-t-il réellement ici?

Nous utilisons ET au niveau du bit pour masquer tous les bits de signe superflus (les 1 à gauche des 8 bits les moins significatifs.) Lorsqu'un entier est converti en octet, Java coupe les 24 bits les plus à gauche.

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Puisque le 32ème bit est maintenant le bit de signe au lieu du 8ème bit (et que nous mettons le bit de signe à 0, ce qui est positif), les 8 bits d'origine de l'octet sont lus par Java comme une valeur positive.

Wayne
la source
1
bravo, la meilleure explication à ce sujet, Wayne! Je cherche juste la formalisation mathématique pour laquelle dans une représentation en complément à deux, le bit de signe peut être copié à droite afin d'ajouter des bits. Il est facile de le comprendre en pensant à la règle de la façon d'obtenir le négatif d'un nombre. c'est-à-dire: considérez tous les bits de droite à gauche et écrivez-les inchangés jusqu'au premier 1 compris. Puis inversez les bits suivants. Si je considère que le bit manquant est 0, il est facile de comprendre qu'ils vont tous à 1. Mais je cherchais une explication plus «mathématique».
AgostinoX
Ce qui signedByte & (0xff)se passe ici est qu'il 0xffs'agit d'un littéral entier, donc signedByte est promu en entier avant que l'opération au niveau du bit ne soit effectuée.
Kevin Wheeler
Ce n'est pas 0xFF, c'est 0x7E dans votre exemple!
JohnyTex
89

132en chiffres ( base 10 ) est 1000_0100en bits ( base 2 ) et Java stocke inten 32 bits:

0000_0000_0000_0000_0000_0000_1000_0100

L'algorithme pour int-to-byte est tronqué à gauche; L'algorithme pour System.out.printlnest un complément à deux (le complément à deux est si le bit le plus à gauche est 1, interprété comme un complément à un négatif (bits inversés ) moins un.); Ainsi System.out.println(int-to-byte( ))est:

  • interpréter comme (si le bit le plus à gauche est 1 [négatif (inverser les bits (moins un (] tronquer à gauche ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = interpréter comme (si le bit le plus à gauche est 1 [négatif (inverser les bits (moins un (] 1000_0100[)))])
  • = interpréter comme (négatif (inverser les bits (moins un ( 1000_0100))))
  • = interpréter comme (négatif (invert-bits ( 1000_0011)))
  • = interpréter comme (négatif ( 0111_1100))
  • = interpréter comme (négatif (124))
  • = interpréter comme (-124)
  • = -124 Tada !!!
Pacerier
la source
7
Très bien expliqué
ZAJ
1
Donc maintenant 132 en décimal est -124 en octet. Comment fonctionne l'inverse?
Nilesh Deokar
@NileshDeokar, L'inverse est par POLA puisqu'ils s'adaptent (; cf JLS 5.1.2 ); la sortie coïncide avec le signe-leftpad ( 0pour le positif et 1pour le négatif).
Pacerier
Qu'est-ce que POLA? La conversion de inten a byteest une conversion avec perte (c'est-à-dire que l'information est perdue). Par conséquent, il n'existe aucun moyen de le reconvertir dans sa intvaleur d' origine .
truthadjustr le
23

l'octet en Java est signé, il a donc une plage de -2 ^ 7 à 2 ^ 7-1 - c'est-à-dire de -128 à 127. Puisque 132 est au-dessus de 127, vous finissez par revenir à 132-256 = -124. Autrement dit, essentiellement 256 (2 ^ 8) sont ajoutés ou soustraits jusqu'à ce qu'ils tombent dans la plage.

Pour plus d'informations, vous pouvez consulter le complément à deux .

bdonlan
la source
16

132 est en dehors de la plage d'un octet qui est de -128 à 127 (Byte.MIN_VALUE à Byte.MAX_VALUE) Au lieu de cela, le bit supérieur de la valeur 8 bits est traité comme le signe qui indique qu'il est négatif dans ce cas. Donc, le nombre est 132 - 256 = -124.

Peter Lawrey
la source
5

voici une méthode très mécanique sans les théories distrayantes:

  1. Convertissez le nombre en représentation binaire (utilisez une calculatrice ok?)
  2. Copiez uniquement les 8 bits les plus à droite (LSB) et supprimez le reste.
  3. À partir du résultat de l'étape 2, si le bit le plus à gauche est 0, utilisez une calculatrice pour convertir le nombre en décimal. Ceci est votre réponse.
  4. Sinon (si le bit le plus à gauche est 1), votre réponse est négative. Laissez tous les zéros les plus à droite et le premier bit différent de zéro inchangés. Et inversé le reste, c'est-à-dire remplacer les 1 par des 0 et les 0 par des 1. Utilisez ensuite une calculatrice pour convertir en décimal et ajoutez un signe négatif pour indiquer que la valeur est négative.

Cette méthode plus pratique est conforme aux réponses très théoriques ci-dessus. Donc, ceux qui lisent encore ces livres Java disant d'utiliser modulo, c'est définitivement faux car les 4 étapes que j'ai décrites ci-dessus ne sont certainement pas une opération modulo.

truthadjustr
la source
Quels livres Java disent d'utiliser 'modulo'? Je n'ai jamais vu de livre de CS affirmer cela en 46 ans, sans parler de livre Java. Qu'est-ce que «modulo»? Il n'y a pas d'opération modulo en Java. Seul un opérateur de reste.
Marquis of Lorne
grep plus dur. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfpage 59
truthadjustr
4

Équation du complément à deux:

entrez la description de l'image ici


En Java, byte (N = 8) etint (N = 32) sont représentés par le complément 2s illustré ci-dessus.

D'après l'équation, un 7 est négatif pour bytemais positif pour int.

coef:   a7    a6  a5  a4  a3  a2  a1  a0
Binary: 1     0   0   0   0   1   0   0
----------------------------------------------
int:    128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 =  132
byte:  -128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 = -124
bcorso
la source
2

souvent dans les livres, vous trouverez l'explication du transtypage d'un entier à un octet comme étant effectué par division de module. ce n'est pas strictement correct comme indiqué ci-dessous ce qui se passe réellement est que les 24 bits les plus significatifs de la valeur binaire du nombre int sont rejetés, laissant une confusion si le bit le plus à gauche restant est défini, ce qui désigne le nombre comme négatif

public class castingsample{

public static void main(String args[]){

    int i;
    byte y;
    i = 1024;
    for(i = 1024; i > 0; i-- ){

      y = (byte)i;
      System.out.print(i + " mod 128 = " + i%128 + " also ");
      System.out.println(i + " cast to byte " + " = " + y);

    }

}

}
stylo
la source
2
Je n'ai jamais vu cela dans aucun livre en 46 ans.
Marquis of Lorne
2

Un algorithme rapide qui simule la façon dont cela fonctionne est le suivant:

public int toByte(int number) {
    int tmp = number & 0xff
    return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}

Comment ça marche? Regardez la réponse de daixtr . Une implémentation de l'algorithme exact décrit dans sa réponse est la suivante:

public static int toByte(int number) {
    int tmp = number & 0xff;
    if ((tmp & 0x80) == 0x80) {
        int bit = 1;
        int mask = 0;
        for(;;) {
            mask |= bit;
            if ((tmp & bit) == 0) {
                bit <<=1;
                continue;
            }
            int left = tmp & (~mask);
            int right = tmp & mask;
            left = ~left;
            left &= (~mask);
            tmp = left | right;
            tmp = -(tmp & 0xff);
            break;
        }
    }
    return tmp;
}
Francisco Neto
la source
1

Si vous voulez comprendre cela mathématiquement, comme comment cela fonctionne

donc fondamentalement les nombres b / w -128 à 127 seront écrits de la même manière que leur valeur décimale, au-dessus de cela (votre nombre - 256).

par exemple. 132, la réponse sera 132 - 256 = - 124 ie

256 + votre réponse au nombre 256 + (-124) est 132

Un autre exemple

double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b; System.out.println(c + " " + d);

la sortie sera 39 44

(295 à 256) (300 à 256)

REMARQUE: il ne tiendra pas compte des nombres après la virgule.

Prakhar Lohiya
la source
0

Conceptuellement, des soustractions répétées de 256 sont faites à votre nombre, jusqu'à ce qu'il soit compris entre -128 et +127. Donc, dans votre cas, vous commencez par 132, puis vous terminez par -124 en une seule étape.

Sur le plan informatique, cela correspond à l'extraction des 8 bits les moins significatifs de votre numéro d'origine. (Et notez que le bit le plus significatif de ces 8 devient le bit de signe.)

Notez que dans d'autres langages, ce comportement n'est pas défini (par exemple C et C ++).

Bathsheba
la source
Pour être clair, le résultat obtenu est le même que si des soustractions répétées avaient été effectuées. En pratique, la JVM ne le fait pas de cette façon. (Ce serait horriblement inefficace!)
Stephen C
En effet. J'espère que mon deuxième paragraphe explique comment la JVM fait cela. Mais j'ai un peu bidouillé ma langue.
Bathsheba
1
Oui. Le changement de «essentiellement» en «conceptuellement» fait une énorme différence!
Stephen C
-1
 N is input number
case 1: 0<=N<=127  answer=N;
case 2: 128<=N<=256 answer=N-256 
case 3: N>256   
        temp1=N/256;
        temp2=N-temp*256;
        if temp2<=127   then answer=temp2;
        else if temp2>=128  then answer=temp2-256;
case 4: negative  number input
        do same procedure.just change the sign of the solution           
diable_29
la source
La réponse correcte est obtenue par masquage de bits, non par division et reste.
Marquis of Lorne