Possiamo creare byte senza segno in Java


185

Sto cercando di convertire un byte con segno in unsigned. Il problema è che i dati che sto ricevendo sono senza segno e Java non supporta byte senza segno, quindi quando legge i dati li tratta come firmati.

Ho provato a convertirlo con la seguente soluzione che ho ottenuto da Stack Overflow.

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

Ma quando viene nuovamente convertito in byte, ottengo gli stessi dati firmati. Sto cercando di utilizzare questi dati come parametro per una funzione di Java che accetta solo un byte come parametro, quindi non posso usare nessun altro tipo di dati. Come posso risolvere questo problema?


2
Guava: UnsignedBytes.toint (valore byte)
jacktrades

20
java.lang.Byte.toUnsignedInt (valore byte);
themarketka,

Risposte:


107

Non sono sicuro di aver capito la tua domanda.

Ho appena provato questo e per byte -12 (valore con segno) ha restituito un numero intero 244 (equivalente a un valore con byte senza segno ma digitato come int):

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

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

È quello che vuoi fare?

Java non consente di esprimere 244 come bytevalore, come farebbe C. Per esprimere numeri positivi sopra Byte.MAX_VALUE(127) è necessario utilizzare un altro tipo intero, come short, into long.


1
byte b = (byte)unsignedToBytes((byte) -12); ora prova a stampare b
Jigar Joshi il

101
Perché hai accettato questa come risposta corretta? Tutto ciò che fa è esattamente lo stesso del metodo menzionato nella tua domanda: convertire un byte in un numero intero senza segno.
Adamski,

1
A volte è importante avere valori con segno, a volte senza segno, quindi probabilmente questo è il motivo per cui ha accettato questa risposta. (byte) (b & 0xff) non ha alcun senso, ma (byte) (Math.min ((b & 0xff) * 2, 255)) ha un senso, ad esempio in computer grafica renderà il pixel rappresentato da il byte due volte più luminoso. :-)
iirekm

3
Potrebbe anche essere chiamato byteToUnsigned
Hernán Eche il

195

Il fatto che le primitive siano firmate in Java è irrilevante per il modo in cui sono rappresentate in memoria / transito: un byte è solo 8 bit e se lo interpretate come un intervallo firmato o no dipende da voi. Non esiste una bandiera magica per dire "questo è firmato" o "questo non è firmato".

Man mano che le primitive sono firmate, il compilatore Java ti impedirà di assegnare un valore superiore a +127 a un byte (o inferiore a -128). Tuttavia, non c'è nulla che ti impedisca di eseguire il downcasting di un int (o short) per raggiungere questo obiettivo:

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"
}

5
Per molte operazioni non fa alcuna differenza, tuttavia per alcune operazioni lo fa. In entrambi i casi è possibile utilizzare un byte come non firmato o utilizzare char che non è firmato.
Peter Lawrey,

62
L'accesso a un array con un numero potenzialmente negativo non è irrilevante.
Stefan,

3
@Stefan - intendevo irrilevante nel contesto di come sono rappresentati sul filo.
Adamski,

6
Il che è in qualche modo irrilevante per la domanda. Dato che ha detto che deve passarlo a una funzione che accetta solo parametri di byte, non importa il tempo, lo interpretiamo come la rappresentazione di byte di un unicorno. Java lo tratterà sempre come un numero con segno, che può essere problematico per un esempio quando questa funzione utilizza il parametro come indice. Tuttavia, per essere onesti, ho anche votato in negativo le altre 2 risposte migliori, dal momento che non rispondono nemmeno alla domanda.
Stefan,

2
@Stefan +1 per te. Assolutamente rilevante se si utilizza il byte per accedere a una matrice di 256 elementi. Questo è un ottimo esempio per dimostrare perché tutti dovrebbero iniziare a studiare C e C ++ prima di passare a Java o C #
Gianluca Ghettini,

46

Guida completa per lavorare con byte senza segno in Java:

Byte senza segno in Java

(Fonte per questa risposta.)


Java Language non fornisce nulla di simile alla unsignedparola chiave. Un bytesecondo le specifiche del linguaggio rappresenta un valore compreso tra -128 e 127. Ad esempio, se un byteviene cast su un intJava interpreterà il primo bit come segno e utilizzerà l' estensione del segno .

Detto questo, nulla ti impedisce di visualizzare bytesemplicemente 8 bit e interpretarli come un valore compreso tra 0 e 255. Tieni presente che non c'è niente che puoi fare per forzare la tua interpretazione sul metodo di qualcun altro. Se un metodo accetta a byte, allora quel metodo accetta un valore compreso tra -128 e 127, salvo diversa indicazione esplicita.

Ecco un paio di utili conversioni / manipolazioni per la tua convenienza:

Conversioni da / a 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

(Oppure, se sei su Java 8+, usa Byte.toUnsignedInt.)

Analisi / formattazione

Il modo migliore è utilizzare le conversioni di cui sopra:

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

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

Aritmetica

La rappresentazione a 2 complementi "funziona" per addizione, sottrazione e moltiplicazione:

// 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 divisione richiede la conversione manuale degli operandi:

byte ratio = (byte) ((b1 & 0xFF) / (b2 & 0xFF));

1
'char' non rappresenta un numero.
disconnessione il

26
Per farla breve: ti sbagli .
aioobe,

36

Non ci sono byte senza segno primitivi in ​​Java. La solita cosa è lanciarlo su un tipo più grande:

int anUnsignedByte = (int) aSignedByte & 0xff;

Il cast è necessario per un int?
nich

Può essere un cast implicito, ma c'è un cast in entrambi i modi. E quel cast ha un'estensione firmata. E questo è un problema. Se esegui un cast esplicito, puoi almeno vedere che sta succedendo.
foo


4

Una nota a margine, se vuoi stamparlo, puoi semplicemente dire

byte b = 255;
System.out.println((b < 0 ? 256 + b : b));

6
perché così complesso? println(b & 0xff)è sufficiente
phuclv il

0

Se pensi di cercare qualcosa di simile.

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

0

Adamski ha fornito la risposta migliore, ma non è del tutto completa, quindi leggi la sua risposta, in quanto spiega i dettagli che non sono.

Se si dispone di una funzione di sistema che richiede il passaggio di un byte senza segno, è possibile passare un byte con segno poiché lo tratterà automaticamente come un byte senza segno.

Pertanto, se una funzione di sistema richiede quattro byte, ad esempio 192 168 0 1 come byte senza segno, è possibile passare -64 -88 0 1 e la funzione continuerà a funzionare, poiché l'atto di passarli alla funzione li annullerà la firma .

Tuttavia è improbabile che tu abbia questo problema poiché le funzioni di sistema sono nascoste dietro le classi per la compatibilità multipiattaforma, anche se alcuni dei metodi di lettura java.io restituiscono byte non sospesi come int.

Se vuoi vederlo funzionare, prova a scrivere byte firmati in un file e rileggili come byte non firmati.


1
Non esistono byte firmati o non firmati.
Vlastimil Ovčáčík,

Come scrivevi e leggevi esattamente i byte nel tuo esempio?
Vlastimil Ovčáčík,

0

Puoi anche:

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

Spiegazione:

diciamo a = (byte) 133;

In memoria è memorizzato come: "1000 0101" (0x85 in esadecimale)

Quindi la sua rappresentazione traduce unsigned = 133, firmato = -123 (come complemento di 2)

a << 24

Quando lo spostamento a sinistra viene eseguito 24 bit a sinistra, il risultato è ora un numero intero di 4 byte che è rappresentato come:

"10000101 00000000 00000000 00000000" (o "0x85000000" in esadecimale)

Poi abbiamo

(a << 24) >>> 24

e si sposta nuovamente a destra di 24 bit ma si riempie di zeri iniziali. Quindi risulta a:

"00000000 00000000 00000000 10000101" (o "0x00000085" in esadecimale)

e questa è la rappresentazione senza segno che equivale a 133.

Se provassi a trasmettere, a = (int) a; ciò che accadrebbe è che mantiene la rappresentazione del complemento di 2 di byte e la memorizza come int anche come complemento di 2:

(int) "10000101" ---> "11111111 11111111 11111111 10000101"

E questo si traduce in: -123


2
Nel 2019, questo non è necessario. Basta usare java.lang.Byte.toUnsignedInt(byte value). E se non stai ancora utilizzando Java 8, aggiorna il prima possibile. Java 7 e precedenti sono fuori uso.
Stephen C

0

Sto cercando di utilizzare questi dati come parametro per una funzione di Java che accetta solo un byte come parametro

Questo non è sostanzialmente diverso da una funzione che accetta un numero intero a cui si desidera passare un valore maggiore di 2 ^ 32-1.

Sembra che dipende da come viene definita e documentata la funzione; Vedo tre possibilità:

  1. Può documentare esplicitamente che la funzione considera il byte come un valore senza segno, nel qual caso la funzione dovrebbe probabilmente fare quello che ti aspetti ma sembra essere implementata in modo errato. Per il caso intero, la funzione dichiarerebbe probabilmente il parametro come intero senza segno, ma ciò non è possibile per il caso byte.

  2. Potrebbe documentare che il valore di questo argomento deve essere maggiore di (o forse uguale a) zero, nel qual caso si sta abusando della funzione (passando un parametro fuori range), aspettandosi che faccia più di quanto non fosse progettato per fare. Con un certo livello di supporto per il debug potresti aspettarti che la funzione generi un'eccezione o fallisca un'asserzione.

  3. La documentazione non può dire nulla, nel qual caso un parametro negativo è, beh, un parametro negativo e se ciò ha un significato dipende da cosa fa la funzione. Se questo non ha senso, allora forse la funzione dovrebbe davvero essere definita / documentata come (2). Se questo è significativo in modo inopportuno (ad esempio, i valori non negativi vengono utilizzati per indicizzare in un array e i valori negativi vengono utilizzati per indicizzare indietro dalla fine dell'array, quindi -1 significa l'ultimo elemento) la documentazione dovrebbe dire cosa significa e mi aspetterei che non è quello che vuoi che faccia comunque.


Hmmm, penso di aver appena pubblicato una risposta che era destinata a un'altra domanda sulla firma dei byte, ma suppongo che sia ancora un po 'rilevante anche qui ...
Kevin Martin,

-1

Se hai una funzione a cui deve essere passato un byte con segno, cosa ti aspetti che faccia se passi un byte senza segno?

Perché non puoi usare nessun altro tipo di dati?

Insolitamente è possibile utilizzare un byte come byte senza segno con traduzioni semplici o assenti. Tutto dipende da come viene utilizzato. Dovresti chiarire che cosa intendi farci.


-1

Sebbene possa sembrare fastidioso (proveniente da C) che Java non includa byte senza segno nella lingua, in realtà non è un grosso problema poiché una semplice operazione "b & 0xFF" produce il valore senza segno per il byte (firmato) b nel (raro) situazioni che sono effettivamente necessarie. I bit in realtà non cambiano - solo l'interpretazione (che è importante solo quando si eseguono ad esempio alcune operazioni matematiche sui valori).


guarda gli altri rispondere, pensi che la tua risposta sia la migliore / utile? descrivilo in breve e aggiungilo nei commenti
Jubin Patel

8
Non è raro solo perché non l'hai trovato. Prova a implementare un protocollo e ti imbatterai in questo milione di volte. La cosa fastidiosa è che la stragrande maggioranza dei casi di utilizzo che ho riscontrato riguardano i byte, vuoi trattare i byte senza segno (perché sono byte, non numeri). La cosa folle è che QUALSIASI operazione bit a bit la convertirà in un int, il che significa che qualsiasi valore "negativo" sarà completamente diverso quando esteso. Sì, puoi aggirarlo sempre mascherando, ma è una perdita di tempo, un processore e causa bug davvero oscuri se lo dimentichi.
Thor84no,

Sono d'accordo con Thor84no: i byte non sono numeri e non dovrebbero avere segno. Dall'altro lato, dal momento che non sono numeri, non dovremmo nemmeno avere / usare gli operatori + e -. Usare solo operatori bit per bit funziona bene, dall'altro lato gli operatori di shift non funzionano come si vorrebbe, e in effetti java promuove un byte spostato in un int.
user1708042

1
@ VlastimilOvčáčík In questo caso è letteralmente impossibile, questa è la cosa agitante. Ripeti OVUNQUE x & 0xFFovunque ne hai bisogno o ripeti qualcosa come behaveLikeAnUnsignedByte(x)ovunque. Questo è necessario per ogni singolo posto in cui usi un valore di byte o un array di byte che deve essere senza segno, non esiste un modo concepibile per evitare questa ripetizione. Non è possibile scrivere l'implementazione di un protocollo che legge e scrive valori di byte con un solo riferimento a una variabile di byte. La tua visione semplicistica potrebbe spiegare perché non si sono mai preoccupati di risolverlo.
Thor84no,

-1

Non esiste alcun byte senza segno in Java, ma se si desidera visualizzare un byte, è possibile eseguire,

int myInt = 144;

byte myByte = (byte) myInt;

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

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

Produzione:

myChar : 90

Per ulteriori informazioni, consultare Come visualizzare un valore esadecimale / byte in Java .


Non è necessario definirlo tu stesso. java.lang.Byte.toUnsignedInt(byte value);esiste per questo.
Alexander - Ripristina Monica il

-2

Secondo le limitazioni in Java, il byte senza segno è quasi impossibile nell'attuale formato del tipo di dati. Puoi scegliere altre librerie di un'altra lingua per ciò che stai implementando e poi puoi chiamarle usando JNI .


Non penso che voglia memorizzarlo come byte con segno. Lo sta ricevendo come byte con segno e vuole memorizzarlo come int, che è perfettamente valido. Il suo problema è che ovunque riceva input rappresenta un valore compreso tra 0 e 255 come byte, ma Java lo interpreta come un complemento a due come valore con segno poiché java non supporta byte con segno.
Zac,

-2

Sì e no. Sono stato alla ricerca di questo problema. Come ho capito questo:

Il fatto è che java ha firmato l'interger da -128 a 127 .. È possibile presentare un unsigned in java con:

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

Se per esempio aggiungi -12 il numero con segno per essere senza segno ottieni 244. Ma puoi usare di nuovo quel numero in firmato, deve essere spostato di nuovo in firmato e sarà di nuovo -12.

Se provi ad aggiungere 244 al java byte, otterrai OutfIndexException.

Saluti..


3
Non è necessario definirlo tu stesso. java.lang.Byte.toUnsignedInt(byte value);esiste per questo.
Alexander - Ripristina Monica il

-3

Se vuoi byte non firmati in Java, sottrai solo 256 dal numero che ti interessa. Produrrà il complemento a due con un valore negativo, che è il numero desiderato in byte non firmati.

Esempio:

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.

È necessario utilizzare tali hack sporchi quando si utilizza leJOS per programmare il mattoncino NXT .


Ti rendi conto che il valore binario di 255 è anche 1111 1111, quindi non è necessaria alcuna sottrazione, giusto?
Nick White,

@NickWhite, sì in binario. Ma java usa il complemento di 2 dove 255 non è 11111111
XapaJIaMnu

Mi dispiace, ma questo è solo sbagliato. Prova alcuni esperimenti. Il valore in speed_unsignedè firmato. Stampalo e vedi. (E - 256qui non ottiene nulla.)
Stephen C
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.