Deklarowanie niepodpisanej int w Javie


316

Czy istnieje sposób na zadeklarowanie niepodpisanej int w Javie?

Albo pytanie może zostać sformułowane w ten sposób: Co to jest odpowiednik Java bez znaku?

Żeby powiedzieć kontekst, w którym patrzyłem na implementację Javy String.hashcode(). Chciałem przetestować możliwość kolizji, gdyby liczba całkowita wynosiła 32 bez znaku int.


7
W Javie nie ma żadnych niepodpisanych typów.
Andrew Logvinov

1
Ten post może ci pomóc stackoverflow.com/a/4449161/778687
tusar

2
To nie wydaje się być AFAICT. Powiązane: stackoverflow.com/questions/430346/…
James Manning

11
To zależy od celu, który próbujesz osiągnąć. W większości przypadków wszystkie liczby całkowite w Javie są podpisane. Możesz jednak traktować liczbę całkowitą ze znakiem jako bez znaku w jednym konkretnym przypadku: możesz przesunąć w prawo bez rozszerzania znaku, używając >>>operatora zamiast >>.
dasblinkenlight

Odpowiedzi:


310

Java nie ma typu danych dla liczb całkowitych bez znaku .

Możesz zdefiniować longzamiast, intjeśli chcesz przechowywać duże wartości.

Możesz także użyć podpisanej liczby całkowitej, tak jakby była niepodpisana. Zaletą reprezentacji uzupełnienia dwóch jest to, że większość operacji (takich jak dodawanie, odejmowanie, mnożenie i przesunięcie w lewo) jest identyczna na poziomie binarnym dla liczb całkowitych ze znakiem i bez znaku. Kilka operacji (podział, przesunięcie w prawo, porównanie i rzutowanie) są jednak różne. Począwszy od Java SE 8, nowe metody w Integerklasie pozwalają w pełni wykorzystać inttyp danych do wykonania arytmetyki bez znaku :

W Javie SE 8 i nowszych można użyć typu danych int, aby reprezentować 32-bitową liczbę całkowitą bez znaku, która ma minimalną wartość 0 i maksymalną 2 ^ 32-1. Użyj klasy Integer, aby użyć typu danych int jako liczby całkowitej bez znaku. Do klasy Integer dodano metody statyczne, takie jak itp. compareUnsigned, W divideUnsignedcelu obsługi operacji arytmetycznych dla liczb całkowitych bez znaku.

Zauważ, że intzmienne są nadal podpisywane, gdy są deklarowane, ale arytmetyka bez znaku jest teraz możliwa przy użyciu tych metod w Integerklasie.


11
Szczerze mówiąc, w przypadku wielu projektów wymagania techniczne nie są tak surowe i rzeczywiście można sobie pozwolić na „marnowanie” takiej pamięci.
Simeon Visser,

6
Wiem, rozumiem też pierwotny cel Javy. Ale na przykład smartfony nie dysponują dodatkową pamięcią. I o ile wiem, zwykle używają Javy. Ale cóż, nie chcę rozpoczynać wojny między programistami Java i innymi.
Tomáš Zato - Przywróć Monikę

122
Dla mnie nie chodzi tylko o marnowanie pieniędzy. Kiedy pracujesz na nieco wyższym poziomie, praca bez znaku jest po prostu łatwiejsza do pracy
Cruncher

24
Od wersji Java 8 nie jest to już prawdą . W Javie SE 8 i nowszych można użyć inttypu danych do reprezentowania 32-bitowej liczby całkowitej bez znaku, która ma minimalną wartość 0i maksymalną wartość 2^32-1. - patrz docs.oracle.com/javase/tutorial/java/nutsandbolts/… i docs.oracle.com/javase/8/docs/api/java/lang/Integer.html
8bitjunkie

2
@ 7SpecialGems: Zaktualizowałem odpowiedź, aby uwzględnić te informacje. Biorąc to pod uwagę, nie jest możliwe zadeklarowanie liczb całkowitych bez znaku lub wykluczenie wartości ujemnych, można użyć tylko inttak, jakby były niepodpisane przy użyciu różnych metod.
Simeon Visser,

70

1
@stas Mam problem ze zrozumieniem użycia i wielka sprawa z możliwością używania niepodpisanych typów danych. Z różnych źródeł online, z których czytam, wydaje się, że obraca się wokół poszerzenia maksymalnej wartości, a domniemana z natury gwarancja, że ​​jest to liczba dodatnia. Czy moje rozumowanie jest prawidłowe, czy są też inne ważne powody? Ponadto, teraz, gdy Integerklasa w Javie 8 pozwala na użycie unsigned int, jest różnica między samą przestrzenią a szybkością (ponieważ w C / C ++ są one prymitywne, podczas gdy w Javie jest to opakowanie na cały obiekt)
Abdul

2
@Abdul - kiedy pracujesz na poziomie bitowym (zwykle ponieważ łączysz się ze sprzętem), musisz wartości, aby zachowywać się w określony sposób. tj. - rollover po 11111111 na 00000000 itd. Używanie podpisanego zamiast niepodpisanego może przerwać obliczenia CRC itp. To nie jest zatrzymanie pokazu, tylko zmarnowany czas.
Lorne K,

3
@Lorne K: w Javie, najeżdżaj int, nawet jeśli są podpisane. Jest to C / C ++, w którym niepodpisany przechodzi, ale podpis powoduje „Niezdefiniowane zachowanie” przy przepełnieniu. Jeśli „przewracanie” jest Twoim jedynym zmartwieniem, nie potrzebujesz niepodpisanego. Myślę, że dlatego procedury CRC itp. Działają w Javie bez dodatkowych wysiłków. I dlatego nowy interfejs API dodaje tylko parsowanie, formatowanie, porównania, dzielenie i resztę. Wszystkie inne operacje, a mianowicie manipulacje bitami, ale także dodawanie, odejmowanie, mnożenie itd. I tak robią właściwą rzecz.
Holger,

4
@Ciprian Tomoiaga: w przypadku dodawania z najazdem wzorce bitowe danych wejściowych i wynik nie zależą od tego, czy interpretujesz je jako liczbę podpisaną, czy liczbę niepodpisaną. Jeśli masz cierpliwość, możesz spróbować ze wszystkimi kombinacjami 2⁶⁵…
Holger

3
@Holger dzięki za wyjaśnienie! Rzeczywiście, okazuje się, że właśnie dlatego używamy uzupełnienia 2. Próbowałem z kilkoma kombinacjami 2 ^ 8 ^^
Ciprian Tomoiagă

66

To, czy wartość w int jest podpisana, czy niepodpisana, zależy od sposobu interpretacji bitów - Java interpretuje bity jako wartość podpisaną (nie ma prymitywów bez znaku).

Jeśli masz liczbę całkowitą, którą chcesz interpretować jako wartość bez znaku (np. Czytasz liczbę całkowitą z DataInputStream, o której wiesz, że zawiera wartość bez znaku), możesz wykonać następującą sztuczkę.

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

Zauważ, że ważne jest, aby literał szesnastkowy był długim literałem, a nie literałem int - stąd „l” na końcu.


3
Dla mnie jest to najlepsza odpowiedź ... Moje dane pochodzą z identyfikatora UID karty NFC, który może mieć 4 lub 8 bajtów ... W przypadku 4 bajtów musiałem rzucić je na bez znaku int i nie mogłem użyj ByteBuffer.getLong, ponieważ nie były to dane 64-bitowe. Dzięki.
Loudenvier

Dlaczego to musi być długie. Nie możesz po prostu zrobić 0xFFFFFFi zachować int?
Niezadowolony

19

Potrzebowaliśmy numery niepodpisane modelować MySQL niepodpisane TINYINT, SMALLINT, INT, BIGINTw jOOQ , dlatego stworzyliśmy jOOU , minimalistyczny oferty biblioteki otoki typów dla niepodpisanych liczb całkowitych w Javie. Przykład:

import static org.joou.Unsigned.*;

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

Wszystkie te typy rozszerzają się java.lang.Numberi można je przekształcić w typy pierwotne wyższego rzędu i BigInteger. Mam nadzieję że to pomoże.

(Zastrzeżenie: Pracuję dla firmy stojącej za tymi bibliotekami)


Brzmi bardzo wygodnie! Dzięki za wzmiankę. :)
Lucas Sousa

7

W przypadku liczb bez znaku możesz użyć tych klas z biblioteki Guava :

Obsługują różne operacje:

  • plus
  • minus
  • czasy
  • mod
  • podzielony przez

W tej chwili wydaje się, że brakuje operatorów zmiany bajtów. Jeśli potrzebujesz, możesz użyć BigInteger z Java.



2

Być może o to ci chodziło?

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

Poświęcasz milionową sekundę wydajności na leniwe pisanie za pomocą operatorów trójskładnikowych zamiast instrukcji if. Niedobrze. (żartuje)
ytpillai

5
Czy naprawdę uważasz, że 2 * (long) Integer.MAX_VALUE + 2łatwiej to zrozumieć niż 0x1_0000_0000L? W związku z tym, dlaczego nie po prostu return signed & 0xFFFF_FFFFL;?
Holger,

2

Wygląda na to, że możesz poradzić sobie z problemem podpisywania, wykonując „logiczne AND” na wartościach przed ich użyciem:

Przykład (wartość byte[] header[0]is 0x86):

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

Wynik:

Integer -122 = 134

2

Tutaj są dobre odpowiedzi, ale nie widzę żadnych demonstracji operacji bitowych. Jak mówi Visser (obecnie akceptowana odpowiedź), Java domyślnie podpisuje liczby całkowite (Java 8 ma liczby całkowite bez znaku, ale nigdy ich nie używałem). Bez zbędnych ceregieli, zróbmy to ...

Przykład RFC 868

Co się stanie, jeśli będziesz musiał zapisać liczbę całkowitą bez znaku w IO? Praktycznym przykładem jest, gdy chcesz wyprowadzić czas zgodnie z RFC 868 . Wymaga to 32-bitowej, dużej liczby całkowitej bez znaku, która koduje liczbę sekund od 00:00 1 stycznia 1900 roku. Jak byś to zakodował?

Stwórz własną 32-bitową liczbę całkowitą bez znaku:

Deklaracja tablicy bajtów 4 bajtów (32 bity)

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

To inicjuje tablicę, patrz Czy tablice bajtów są inicjowane na zero w Javie? . Teraz musisz wypełnić każdy bajt w tablicy informacjami w kolejności big-endian (lub little-endian, jeśli chcesz zniszczyć spustoszenie). Zakładając, że masz długi zawierający czas (długie liczby całkowite w Javie mają 64 bity) secondsSince1900(który wykorzystuje tylko pierwsze 32 bity, a ty poradziłeś sobie z faktem, że data odnosi się do godziny 12:00 1 stycznia 1970), to możesz użyć logicznego AND do wydobycia z niego bitów i przesunięcia tych bitów na pozycje (cyfry), które nie zostaną zignorowane, gdy zostaną wprowadzone w Bajt, w kolejności 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

Nasz my32BitUnsignedIntegerjest teraz równoważny z niepodpisaną 32-bitową liczbą całkowitą big-endian, która jest zgodna ze standardem RCF 868. Tak, długi typ danych jest podpisany, ale zignorowaliśmy ten fakt, ponieważ założyliśmy, że secondSince1900 używał tylko niższych 32 bitów). Z powodu przekształcenia długiego w bajt wszystkie bity większe niż 2 ^ 7 (pierwsze dwie cyfry w kodzie szesnastkowym) zostaną zignorowane.

Źródło odniesienia: Java Network Programming, 4th Edition.


1

Właśnie utworzyłem ten fragment kodu, który konwertuje „this.altura” z liczby ujemnej na dodatnią. Mam nadzieję, że to pomaga komuś w potrzebie

       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

Możesz użyć funkcji Math.abs (liczba). Zwraca liczbę dodatnią.


12
nitpick: nie, jeśli zdaszMIN_VALUE
Dennis Meng

2
@ kyo722 Nie wyobrażam sobie, że to zwróci wartość dodatnią w zakresie prymitywów bez znaku.
Florian R. Klein

1
nitpick # 2: nie, jeśli zdasz0
genisage
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.