Dichiarazione di un int senza segno in Java


316

C'è un modo per dichiarare un int senza segno in Java?

Oppure la domanda può essere formulata anche in questo modo: qual è l'equivalente Java di unsigned?

Giusto per dirti il ​​contesto di cui stavo guardando l'implementazione di Java String.hashcode(). Volevo testare la possibilità di collisione se il numero intero fosse 32 unsigned int.


7
Non ci sono tipi senza segno in Java.
Andrew Logvinov,


2
Non sembra essere un modo AFAICT. Correlati: stackoverflow.com/questions/430346/…
James Manning

11
Dipende dallo scopo che stai cercando di raggiungere. Per la maggior parte degli scopi, tutti gli interi in Java sono firmati. Tuttavia, puoi considerare un intero con segno come non firmato in un caso specifico: puoi spostare a destra senza estendere il segno utilizzando l' >>>operatore anziché >>.
dasblinkenlight,

Risposte:


310

Java non ha un tipo di dati per numeri interi senza segno .

È possibile definire un longanziché un intse è necessario memorizzare valori di grandi dimensioni.

Puoi anche usare un numero intero con segno come se fosse senza segno. Il vantaggio della rappresentazione del complemento a due è che la maggior parte delle operazioni (come addizione, sottrazione, moltiplicazione e spostamento a sinistra) sono identiche a livello binario per numeri interi con o senza segno. Alcune operazioni (divisione, spostamento a destra, confronto e fusione), tuttavia, sono diverse. A partire da Java SE 8, i nuovi metodi nella Integerclasse consentono di utilizzare completamente il inttipo di dati per eseguire l'aritmetica senza segno :

In Java SE 8 e versioni successive, è possibile utilizzare il tipo di dati int per rappresentare un numero intero a 32 bit senza segno, che ha un valore minimo di 0 e un valore massimo di 2 ^ 32-1. Utilizzare la classe Integer per utilizzare il tipo di dati int come numero intero senza segno. Metodi statici come compareUnsigned, divideUnsignedecc. Sono stati aggiunti alla classe Integer per supportare le operazioni aritmetiche per numeri interi senza segno.

Notare che le intvariabili sono ancora firmate quando dichiarate ma l'aritmetica senza segno è ora possibile usando questi metodi nella Integerclasse.


11
Ad essere onesti, per molti progetti i requisiti tecnici non sono così severi e puoi davvero permetterti di "sprecare" la memoria in quel modo.
Simeon Visser,

6
Lo so, capisco anche lo scopo originale di Java. Ad esempio, gli smartphone non dispongono di memoria aggiuntiva. E di solito usano Java, per quanto ne so. Ma bene, non voglio iniziare una guerra tra i programmatori Java e gli altri.
Tomáš Zato - Ripristina Monica il

122
Per me questo non è solo un caso di spreco di denaro. Quando lavori a un po 'di livello, unsigned è semplicemente più semplice con cui lavorare
Cruncher,

24
A partire da Java 8, questo non è più vero . In Java SE 8 e versioni successive, è possibile utilizzare il inttipo di dati per rappresentare un numero intero a 32 bit senza segno, che ha un valore minimo di 0e un valore massimo di 2^32-1. - vedi docs.oracle.com/javase/tutorial/java/nutsandbolts/… e docs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie

2
@ 7SpecialGems: ho aggiornato la risposta per includere tali informazioni. Detto questo, non è possibile dichiarare numeri interi senza segno o escludere valori negativi, è possibile utilizzare un solo intcome se fosse senza segno utilizzando vari metodi.
Simeon Visser,

70

1
@stas Sto avendo difficoltà a capire l'uso e molto di più rispetto alla possibilità di usare tipi di dati non firmati. Dalle varie fonti online da cui ho letto, sembra che giri solo allargando il valore massimo e l'implicito per natura garantisce che sia un numero positivo. La mia comprensione è corretta o ci sono altre ragioni importanti? Inoltre, ora che la Integerclasse in Java 8 consente di utilizzare int senza segno, è la differenza tra spazio e velocità (dato che in C / C ++ sono primitivi, mentre in Java è un intero wrapper di oggetti)
Abdul

2
@Abdul - quando lavori a livello di bit (di solito perché stai interfacciati con l'hardware) hai bisogno di valori per comportarti in un certo modo. vale a dire - passare da 11111111 a 00000000 ecc. L'uso del segno al posto del segno non firmato può interrompere i calcoli del CRC ecc.
Lorne K,

3
@Lorne K: in Java, ints roll over, anche se sono firmati. È C / C ++ in cui viene eseguito il roll over senza segno, ma con segno provoca "Comportamento indefinito" in caso di overflow. Se "ribaltamento" è la tua unica preoccupazione, non hai bisogno di unsigned. Immagino, ecco perché le routine CRC, ecc. Funzionano in Java senza sforzi aggiuntivi. Ed è per questo che la nuova API aggiunge solo analisi, formattazione, confronti, divisione e resto. Tutte le altre operazioni, vale a dire tutte le manipolazioni dei bit, ma anche l'aggiunta, la sottrazione, la moltiplicazione, ecc. Stanno comunque facendo la cosa giusta.
Holger,

4
@Ciprian Tomoiaga: per l'aggiunta con rollover, i pattern di bit dell'input e del risultato non dipendono dal fatto che lo si interpreti come numero con segno o numero senza segno. Se hai pazienza, puoi provarlo con tutte le 2⁶⁵ combinazioni ...
Holger

3
@Holger grazie per la spiegazione! In effetti, è per questo che utilizziamo effettivamente il complemento di 2. L'ho provato con alcune combinazioni 2 ^ 8 ^^
Ciprian Tomoiagă

66

Il fatto che un valore in un int sia firmato o non firmato dipende dalla modalità di interpretazione dei bit: Java interpreta i bit come valore con segno (non ha primitive senza segno).

Se si dispone di un int che si desidera interpretare come valore senza segno (ad esempio, si legge un int da un DataInputStream che si conosce contiene un valore senza segno), è possibile eseguire il trucco seguente.

int fourBytesIJustRead = someObject.getInt();
long unsignedValue = fourBytesIJustRead & 0xffffffffl;

Si noti che è importante che il valore letterale esadecimale sia un valore letterale lungo, non un valore letterale int, quindi la "l" alla fine.


3
Per me questa è la risposta migliore ... I miei dati provengono da un UID della scheda NFC, che può avere 4 o 8 byte ... Nel caso di 4 byte avevo bisogno di trasmetterlo a un int senza segno e non potevo usa ByteBuffer.getLong perché non erano dati a 64 bit. Grazie.
Loudenvier,

Perché deve essere lungo. Non puoi semplicemente fare 0xFFFFFFe mantenere l'int?
Displee il

19

Avevamo bisogno di numeri senza segno a modello di MySQL senza segno TINYINT, SMALLINT, INT, BIGINTin jOOQ , che è il motivo per cui abbiamo creato jOOU , un'offerta libreria minimalista involucro tipi per i numeri interi senza segno in Java. Esempio:

import static org.joou.Unsigned.*;

// and then...
UByte    b = ubyte(1);
UShort   s = ushort(1);
UInteger i = uint(1);
ULong    l = ulong(1);

Tutti questi tipi si estendono java.lang.Numbere possono essere convertiti in tipi primitivi di ordine superiore e BigInteger. Spero che questo ti aiuti.

(Dichiarazione di non responsabilità: lavoro per l'azienda dietro queste biblioteche)


Sembra molto conveniente! Grazie per averlo menzionato. :)
Lucas Sousa,

7

Per i numeri senza segno è possibile utilizzare queste classi dalla libreria Guava :

Supportano varie operazioni:

  • più
  • meno
  • volte
  • mod
  • diviso per

Ciò che sembra mancare al momento sono gli operatori di byte shift. Se ne hai bisogno puoi usare BigInteger da Java.



2

Forse è questo che intendevi?

long getUnsigned(int signed) {
    return signed >= 0 ? signed : 2 * (long) Integer.MAX_VALUE + 2 + signed;
}
  • getUnsigned(0) → 0
  • getUnsigned(1) → 1
  • getUnsigned(Integer.MAX_VALUE) → 2147483647
  • getUnsigned(Integer.MIN_VALUE) → 2147483648
  • getUnsigned(Integer.MIN_VALUE + 1) → 2147483649

Stai sacrificando un milionesimo di secondo di tempo di esibizione per la battitura pigra con operatori ternari invece di istruzioni if. Non bene. (scherzando)
ytpillai,

5
Pensi davvero, 2 * (long) Integer.MAX_VALUE + 2è più facile da capire di 0x1_0000_0000L? A tal proposito, perché non semplicemente return signed & 0xFFFF_FFFFL;?
Holger,

2

Sembra che tu possa gestire il problema della firma facendo un "AND logico" sui valori prima di usarli:

Esempio (valore di byte[] header[0]is 0x86):

System.out.println("Integer "+(int)header[0]+" = "+((int)header[0]&0xff));

Risultato:

Integer -122 = 134

2

Ci sono buone risposte qui, ma non vedo alcuna dimostrazione di operazioni bit per bit. Come dice Visser (la risposta attualmente accettata), Java firma gli interi di default (Java 8 ha numeri interi senza segno, ma non li ho mai usati). Senza ulteriori indugi, facciamolo ...

Esempio RFC 868

Cosa succede se è necessario scrivere un numero intero senza segno su IO? L'esempio pratico è quando si desidera emettere l'ora secondo RFC 868 . Ciò richiede un numero intero senza segno a 32 bit, big-endian, che codifica il numero di secondi a partire dalle 12:00 del 1 ° gennaio 1900. Come lo codificheresti?

Crea il tuo numero intero a 32 bit senza segno in questo modo:

Dichiarare un array di byte di 4 byte (32 bit)

Byte my32BitUnsignedInteger[] = new Byte[4] // represents the time (s)

Questo inizializza l'array, vedi Gli array di byte sono inizializzati su zero in Java? . Ora devi riempire ogni byte dell'array con le informazioni nell'ordine big-endian (o little-endian se vuoi rovinare il caos). Supponendo che tu abbia un lungo che contiene il tempo (i numeri interi lunghi sono lunghi 64 bit in Java) chiamato secondsSince1900(che utilizza solo i primi 32 bit, e hai gestito il fatto che la data fa riferimento 12:00 AM 1 gennaio 1970), quindi puoi usare l'AND logico per estrarre bit da esso e spostare quei bit in posizioni (cifre) che non verranno ignorate quando vengono portate in un byte e in ordine big-endian.

my32BitUnsignedInteger[0] = (byte) ((secondsSince1900 & 0x00000000FF000000L) >> 24); // first byte of array contains highest significant bits, then shift these extracted FF bits to first two positions in preparation for coersion to Byte (which only adopts the first 8 bits)
my32BitUnsignedInteger[1] = (byte) ((secondsSince1900 & 0x0000000000FF0000L) >> 16);
my32BitUnsignedInteger[2] = (byte) ((secondsSince1900 & 0x000000000000FF00L) >> 8);
my32BitUnsignedInteger[3] = (byte) ((secondsSince1900 & 0x00000000000000FFL); // no shift needed

Nostro my32BitUnsignedInteger è ora equivalente a un intero big-endian a 32 bit senza segno che aderisce allo standard RCF 868. Sì, il tipo di dati lungo è firmato, ma abbiamo ignorato questo fatto, poiché abbiamo assunto che i secondi, dal momento che solo il 1900 utilizzava solo i 32 bit inferiori). A causa del passaggio del long in un byte, tutti i bit superiori a 2 ^ 7 (prime due cifre in esadecimale) verranno ignorati.

Fonte di riferimento: Java Network Programming, 4th Edition.


1

Ho appena creato questo pezzo di codice, che converte "this.altura" da un numero negativo a uno positivo. Spero che questo aiuti qualcuno nel bisogno

       if(this.altura < 0){    

                        String aux = Integer.toString(this.altura);
                        char aux2[] = aux.toCharArray();
                        aux = "";
                        for(int con = 1; con < aux2.length; con++){
                            aux += aux2[con];
                        }
                        this.altura = Integer.parseInt(aux);
                        System.out.println("New Value: " + this.altura);
                    }

-19

È possibile utilizzare la funzione Math.abs (numero). Restituisce un numero positivo.


12
nitpick: no se si passaMIN_VALUE
Dennis Meng

2
@ kyo722 Non riesco a immaginare che questo restituirà un valore positivo nella gamma di primitive non firmate.
Florian R. Klein,

1
nitpick # 2: non se si passa0
genisage
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.