Jak używać liczby całkowitej bez znaku w Javie 8 i Java 9?

82

W Oracle „prymitywnych typów danych” strony , to mówi, że Java 8 dodaje wsparcie dla unsigned int i wyroby długie:

int: Domyślnie inttyp danych jest 32-bitową uzupełnienie dwójkowe liczbę całkowitą, która ma wartość minimalną -2 31 i wartość maksymalnej ilości 2 31 -1. W Java SE 8, a następnie, można użyć inttypu danych reprezentuje nieoznaczoną 32-bitową liczbę całkowitą, która ma wartość co najmniej 0 i wartości maksimum 2 32 -1. Użyj Integerklasy, aby użyć inttypu danych jako liczby całkowitej bez znaku. Aby uzyskać więcej informacji, zobacz sekcję Klasy liczbowe. Do klasy dodano metody statyczne, takie jak itp. compareUnsigned, Aby obsługiwać operacje arytmetyczne na liczbach całkowitych bez znaku.divideUnsignedInteger

long: Typ longdanych to 64-bitowa liczba całkowita z uzupełnieniem do dwóch. Podpisany longma wartość minimalną -2 63 i wartość maksymalnej ilości 2 63 -1. W Java SE 8, a następnie, można użyć longtypu danych reprezentuje nieoznaczoną 64-bitowy long, który ma minimalną wartość 0, a wartość maksymalna z 2 64 -1. Użyj tego typu danych, gdy potrzebujesz zakresu wartości szerszego niż te podane przez int. LongKlasa zawiera również metody podoba compareUnsigned, divideUnsignedetc w celu wsparcia operacji arytmetycznych na niepodpisane long.

Jednak nie znajduję sposobu, aby zadeklarować długość bez znaku lub liczbę całkowitą. Na przykład poniższy kod wyświetla komunikat o błędzie kompilatora „literał jest poza zakresem” (używam oczywiście Java 8), kiedy powinien znajdować się w zakresie (przypisana wartość to dokładnie 2 64 −1) :

public class Foo {
    static long values = 18446744073709551615L;
    
    public static void main(String[] args){
        System.out.println(values);
    }  
}

Czy jest więc sposób na zadeklarowanie unsigned int lub long?

Pabce
źródło
2
Co jest zwracane w stałej Long.MAX_VALUE w Javie 8?
Bruno Franco
21
Nie ma liczby całkowitej bez znaku ani typu długiego bez znaku. Jeśli użyjesz jednej z nowych metod, ta metoda potraktuje 32-bitową lub 64-bitową liczbę całkowitą tak, jakby była bez znaku. Ale to wszystko. Typ zmiennej nadal będzie podpisany i od Ciebie zależy, czy użyjesz jej jako liczby bez znaku. Nie dodali niepodpisanych literałów, ale być może dodadzą je do Java 9, jeśli wystarczająco dużo osób je zgłosi. :)
ajb
5
Tak naprawdę niczego nie zmienili, poza dodaniem nowych metod.
Hot Licks
4
O ile wiem, wszystko, co zrobili, to dodanie metod, które mogą zwracać wartości bez znaku, ale nie pozwalają na deklarowanie wartości bez znaku. Trochę głupie, jeśli o mnie chodzi, i to prawdziwy ból. Zastanawiam się, czy jednym ze sposobów byłoby użycie Integer.divideUnsigned z jednym parametrem równym 1, a drugim dowolną liczbą traktowaną jako niepodpisana. Zadziałałoby, o ile wiem, ale wydaje się być naprawdę głupim sposobem robienia rzeczy.
cluemein
@ajb Jak mogę pomóc w procesie „podsłuchiwania”, aby w końcu zobaczyć poprawne niepodpisane w Javie? :)
Matthieu

Odpowiedzi:

51

Zgodnie z dokumentacją, którą opublikowałeś, i tym postem na blogu - nie ma różnicy, kiedy deklarujesz prymityw między bez znaku int / long a podpisanym. „Nowa obsługa” to dodanie metod statycznych w klasach Integer i Long, np . Integer.divideUnsigned . Jeśli nie używasz tych metod, twój „unsigned” długo powyżej 2 ^ 63-1 jest zwykłym starym długim z wartością ujemną.

Z szybkiego przeglądu wynika, że ​​nie ma sposobu na zadeklarowanie stałych całkowitych z zakresu poza +/- 2 ^ 31-1 lub +/- 2 ^ 63-1 dla długości. Trzeba by ręcznie obliczyć wartość ujemną odpowiadającą dodatniej wartości spoza zakresu.

Sbodd
źródło
87

Cóż, nawet w Javie 8 longi intnadal są podpisane, tylko niektóre metody traktują je tak, jakby były niepodpisane . Jeśli chcesz pisać bez znaku longdosłownie w ten sposób, możesz to zrobić

static long values = Long.parseUnsignedLong("18446744073709551615");

public static void main(String[] args) {
    System.out.println(values); // -1
    System.out.println(Long.toUnsignedString(values)); // 18446744073709551615
}
kajacx
źródło
5
Obserwacja, którą widzę w tym przypadku, jest taka, że ​​porządek jest uszkodzony. Gdybym miał dostać niepodpisany długi z jakiegoś systemu (nawiasem mówiąc, Twitter dostarcza takie identyfikatory) i chciałbym je posortować, być może dlatego, że wiedziałem, że są chronologiczne, to wszystko poza Long.MAX_VALUE faktycznie pojawi się przed 0 jako ujemne z implementacją Javy :-(. Zamiast tego wydaje się, że należy przesunąć ją w dół tak, aby mapy bez znaku 0 na podpisane Long.MIN_VALUE.
David Smiley
10
@DavidSmiley Słuszna uwaga. Aby posortować długie bez znaku, użyj Long.compareUnsigned lub przekaż tę metodę jako komparator do sortowania funkcji,
kajacx
26
    // Java 8
    int vInt = Integer.parseUnsignedInt("4294967295");
    System.out.println(vInt); // -1
    String sInt = Integer.toUnsignedString(vInt);
    System.out.println(sInt); // 4294967295

    long vLong = Long.parseUnsignedLong("18446744073709551615");
    System.out.println(vLong); // -1
    String sLong = Long.toUnsignedString(vLong);
    System.out.println(sLong); // 18446744073709551615

    // Guava 18.0
    int vIntGu = UnsignedInts.parseUnsignedInt(UnsignedInteger.MAX_VALUE.toString());
    System.out.println(vIntGu); // -1
    String sIntGu = UnsignedInts.toString(vIntGu);
    System.out.println(sIntGu); // 4294967295

    long vLongGu = UnsignedLongs.parseUnsignedLong("18446744073709551615");
    System.out.println(vLongGu); // -1
    String sLongGu = UnsignedLongs.toString(vLongGu);
    System.out.println(sLongGu); // 18446744073709551615

    /**
     Integer - Max range
     Signed: From −2,147,483,648 to 2,147,483,647, from −(2^31) to 2^31 – 1
     Unsigned: From 0 to 4,294,967,295 which equals 2^32 − 1

     Long - Max range
     Signed: From −9,223,372,036,854,775,808 to 9,223,372,036,854,775,807, from −(2^63) to 2^63 − 1
     Unsigned: From 0 to 18,446,744,073,709,551,615 which equals 2^64 – 1
     */
blueberry0xff
źródło
10
Powinien dodać jakieś wyjaśnienie, a nie tylko wyrzucić kod.
cluemein
22

Nie ma sposobu, w jaki sposób zadeklarować unsigned long int lub w Java 8 lub Java 9. Ale niektóre sposoby ich traktować tak, jakby były niepodpisany, na przykład:

static long values = Long.parseUnsignedLong("123456789012345678");

ale to nie jest deklaracja zmiennej.

1ac0
źródło
3
ranię, czemu nie. mogli łatwo dodać typy uint i ulong
EKanadily
@docesam w tym i tym dokumencie zawiera szczegółowe wyjaśnienie. w skrócie, dodano tylko metody manipulacji bez znaku do klasy Integer. i gdyby mogli łatwo dodać, to nie wiem, dlaczego tego nie zrobią ;-)
1ac0
@ Ladislav DANKO są roztropni ludzie i są inni. nie jestem pewien, czy - w tym przypadku - było to rozważne, czy nie, ale mam wrażenie, że wyrocznia nie jest tak ostrożna.
EKanadily
1
@docesam Nie, nie mogli mieć „łatwych do dodania typów uint i ulong”. Wymagałoby to zmiany specyfikacji JLS, JVM, JNI, wielu bibliotek (takich jak java.util.Arrays), refleksji i wielu innych.
Nayuki
1
@ Michaelangel007 Hej, myślę, że podchodzimy do tego tematu z różnymi założeniami. Masz ochotę wysłać mi maila i porozmawiamy o szczegółach?
Nayuki
4

Jeśli korzystanie z biblioteki innej firmy jest opcją, istnieje jOOU (biblioteka wydzielona z jOOQ ), która oferuje typy opakowań dla liczb całkowitych bez znaku w Javie. To nie jest dokładnie to samo, co posiadanie obsługi typów pierwotnych (a tym samym kodu bajtowego) dla typów bez znaku, ale być może nadal jest wystarczająco dobre dla twojego przypadku użycia.

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.

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

Lukas Eder
źródło