Java 8 Nepotpisana aritmetička podrška

1. Pregled

Od postanka Jave potpisali su se svi numerički tipovi podataka. Međutim, u mnogim je situacijama potrebna upotreba nepotpisanih vrijednosti. Na primjer, ako izbrojimo broj događaja događaja, ne želimo naići na negativnu vrijednost.

Podrška za nepotpisanu aritmetiku napokon je dio JDK-a od verzije 8. Ova podrška došla je u obliku nepotpisanog integriranog API-ja, koji primarno sadrži statičke metode u Cijeli broj i Dugo razreda.

U ovom ćemo uputstvu proučiti ovaj API i dati upute za ispravnu upotrebu nepotpisanih brojeva.

2. Prikazi na razini bita

Da bismo razumjeli kako postupati s potpisanim i nepotpisanim brojevima, pogledajmo prvo njihov prikaz na razini bitova.

U Javi se brojevi kodiraju pomoću komplementarnog sustava. Ovo kodiranje provodi mnoge osnovne aritmetičke operacije, uključujući zbrajanje, oduzimanje i množenje, na isti način, bez obzira jesu li operandi potpisani ili nepotpisani.

Stvari bi trebale biti jasnije s primjerom koda. Radi jednostavnosti koristit ćemo varijable bajt primitivni tip podataka. Operacije su slične za druge integralne numeričke tipove, kao što je kratak, int, ili dugo.

Pretpostavimo da imamo neku vrstu bajt s vrijednošću od 100. Ovaj broj ima binarni prikaz 0110_0100.

Udvostručimo ovu vrijednost:

bajt b1 = 100; bajt b2 = (bajt) (b1 << 1);

Lijevi operator pomicanja u danom kodu pomiče sve bitove u varijabli b1 položaj lijevo, tehnički čineći njegovu vrijednost dvostruko većom. Binarni prikaz varijable b2 tada će biti 1100_1000.

U sustavu nepotpisanog tipa, ova vrijednost predstavlja decimalni broj ekvivalentan 2^7 + 2^6 + 2^3, ili 200. Štoviše, u potpisanom sustavu, krajnji lijevi bit radi kao znakovni bit. Stoga je rezultat -2^7 + 2^6 + 2^3, ili -56.

Brzi test može potvrditi ishod:

assertEquals (-56, b2);

Vidimo da su izračunavanja potpisanih i nepotpisanih brojeva ista. Razlike se pojavljuju samo kad JVM binarni prikaz protumači kao decimalni broj.

Operacije zbrajanja, oduzimanja i množenja mogu raditi s nepotpisanim brojevima bez potrebe za bilo kakvim promjenama u JDK. Ostale operacije, poput usporedbe ili dijeljenja, različito rukuju potpisanim i nepotpisanim brojevima.

Ovdje nastupa API za nepotpisano integriranje.

3. Nepotpisani integrirani API

Unsigned Integer API pruža podršku za argmetiku s nepotpisanim cijelim brojevima u Javi 8. Većina članova ovog API-ja su statičke metode u Cijeli broj i Dugo razreda.

Metode u ovim razredima djeluju slično. Stoga ćemo se usredotočiti na Cijeli broj samo u klasi, izostavljajući Dugo razred za kratkoću.

3.1. Usporedba

The Cijeli broj klasa definira metodu imenovanu compareUnsigned za usporedbu nepotpisanih brojeva. Ova metoda smatra sve binarne vrijednosti nepotpisanima, zanemarujući pojam znakovnog bita.

Počnimo s dva broja na granicama znaka int vrsta podataka:

int pozitivno = Integer.MAX_VALUE; int negativno = Integer.MIN_VALUE;

Ako usporedimo ove brojeve kao potpisane vrijednosti, pozitivan očito je veći od negativan:

int signedComparison = Integer.compare (pozitivno, negativno); assertEquals (1, potpisanaUporedba);

Kada se uspoređuju brojevi kao nepotpisane vrijednosti, krajnje lijevi bit smatra se najznačajnijim bitom umjesto znakovnog bita. Dakle, rezultat je drugačiji, sa pozitivan biti manji od negativan:

int unsignedComparison = Integer.compareUnsigned (pozitivno, negativno); assertEquals (-1, unsignedComparison);

Trebalo bi biti jasnije ako pogledamo binarni prikaz tih brojeva:

  • MAX_VALUE ->0111_1111_…_1111
  • MIN_VALUE ->1000_0000_…_0000

Kada je krajnji lijevi bit redoviti bit vrijednosti, MIN_VALUE je jedna jedinica veća od MAX_VALUE u binarnom sustavu. Ovaj test potvrđuje da:

assertEquals (negativan, pozitivan + 1);

3.2. Divizija i Modulo

Baš kao operacija usporedbe, nepotpisane podjele i modulo operacije obrađuju sve bitove kao bitove vrijednosti. Količnici i ostaci su zato različiti kada izvodimo ove operacije s potpisanim i nepotpisanim brojevima:

int pozitivno = Integer.MAX_VALUE; int negativno = Integer.MIN_VALUE; assertEquals (-1, negativno / pozitivno); assertEquals (1, Integer.divideUnsigned (negativno, pozitivno)); assertEquals (-1, negativni% pozitivni); assertEquals (1, Integer.remainderUnsigned (negativan, pozitivan));

3.3. Raščlanjivanje

Pri raščlanjivanju a Niz koristiti parseUnsignedInt metoda, argument teksta može predstavljati broj veći od MAX_VALUE.

Takva velika vrijednost ne može se raščlaniti s raščlaniti metoda, koja može obraditi samo tekstualni prikaz brojeva iz MIN_VALUE do MAX_VALUE.

Sljedeći test provjerava rezultate raščlanjivanja:

Bačeno bacanje = catchThrowable (() -> Integer.parseInt ("2147483648")); assertThat (bačen) .isInstanceOf (NumberFormatException.class); assertEquals (Integer.MAX_VALUE + 1, Integer.parseUnsignedInt ("2147483648"));

Primijetite da parseUnsignedInt metoda može raščlaniti niz koji označava broj veći od MAX_VALUE, ali neće uspjeti raščlaniti nijedan negativan prikaz.

3.4. Oblikovanje

Slično raščlanjivanju, kod formatiranja broja, nepotpisana operacija sve bitove smatra bitovima. Slijedom toga, možemo proizvesti tekstualni prikaz broja otprilike dvostruko većeg od MAX_VALUE.

Sljedeći test potvrđuje rezultat formatiranja MIN_VALUE u oba slučaja - potpisano i nepotpisano:

String signedString = Integer.toString (Integer.MIN_VALUE); assertEquals ("- 2147483648", signedString); Niz unsignedString = Integer.toUnsignedString (Integer.MIN_VALUE); assertEquals ("2147483648", unsignedString);

4. Prednosti i nedostaci

Mnogi programeri, posebno oni koji dolaze iz jezika koji podržava nepotpisane vrste podataka, poput C, pozdravljaju uvođenje nepodpisanih aritmetičkih operacija. Međutim, ovo nije nužno dobra stvar.

Dva su glavna razloga za potražnju za nepotpisanim brojevima.

Prvo, postoje slučajevi kod kojih se negativna vrijednost nikada ne može dogoditi, a upotreba nepotpisane vrste može uopće spriječiti takvu vrijednost. Drugo, s nepotpisanim tipom možemo udvostručite raspon korisnih pozitivnih vrijednosti u usporedbi s potpisanim kolegom.

Analizirajmo obrazloženje žalbe za nepotpisane brojeve.

Kada varijabla uvijek treba biti negativna, vrijednost manja od 0 može biti zgodan u naznačavanju iznimne situacije.

Na primjer, String.indexOf metoda vraća položaj prvog pojavljivanja određenog znaka u nizu. Indeks -1 može lako označiti odsutnost takvog znaka.

Drugi razlog nepotpisanih brojeva je proširenje prostora vrijednosti. Međutim, ako raspon potpisanog tipa nije dovoljan, malo je vjerojatno da bi udvostručeni raspon bio dovoljan.

U slučaju da vrsta podataka nije dovoljno velika, trebamo upotrijebiti drugu vrstu podataka koja podržava mnogo veće vrijednosti, poput upotrebe dugo umjesto int, ili BigInteger rađe nego dugo.

Još jedan problem s Unsigned Integer API je taj što je binarni oblik broja isti, bez obzira je li potpisan ili nepotpisan. Stoga je lako se miješaju potpisane i nepotpisane vrijednosti, što može dovesti do neočekivanih rezultata.

5. Zaključak

Podrška za nepotpisanu aritmetiku u Javi došla je na zahtjev mnogih ljudi. Međutim, blagodati koje donosi nisu jasne. Trebali bismo biti oprezni kada koristimo ovu novu značajku kako bismo izbjegli neočekivane ishode.

Kao i uvijek, izvorni kod za ovaj članak dostupan je na GitHubu.