Java 8 Nepodpísaná aritmetická podpora

1. Prehľad

Od úsvitu Javy sú podpísané všetky číselné dátové typy. V mnohých situáciách sa však vyžaduje použitie nepodpísaných hodnôt. Napríklad, ak spočítame počet výskytov udalosti, nechceme sa stretnúť so zápornou hodnotou.

Podpora nepodpísanej aritmetiky bola od verzie 8 konečne súčasťou JDK. Táto podpora prišla vo forme API Unsigned Integer API, ktoré primárne obsahovalo statické metódy v Celé číslo a Dlhé triedy.

V tomto tutoriáli si prejdeme toto API a dáme pokyny, ako správne používať nepodpísané čísla.

2. Zastúpenia na bitovej úrovni

Aby sme pochopili, ako zaobchádzať s podpísanými a nepodpísanými číslami, najskôr sa pozrime na ich zastúpenie na bitovej úrovni.

V Jave sú čísla kódované pomocou systému dvoch doplnkov. Toto kódovanie implementuje mnoho základných aritmetických operácií vrátane sčítania, odčítania a násobenia rovnakým spôsobom, či už sú operandy podpísané alebo nepodpísané.

Veci by mali byť jasnejšie na príklade kódu. Kvôli jednoduchosti použijeme premenné z bajt primitívny dátový typ. Operácie sú podobné pre iné integrálne numerické typy, ako napr krátky, intalebo dlho.

Predpokladajme, že máme nejaký typ bajt s hodnotou 100. Toto číslo má binárne zastúpenie 0110_0100.

Zdvojnásobme túto hodnotu:

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

Operátor ľavého posunu v danom kóde posúva všetky bity v premennej b1 pozíciu vľavo, čo technicky zvyšuje jeho hodnotu dvakrát väčšiu. Binárne znázornenie premennej b2 potom bude 1100_1000.

V systéme typu bez znamienka predstavuje táto hodnota desatinné číslo zodpovedajúce 2^7 + 2^6 + 2^3alebo 200. Napriek tomu v podpísanom systéme funguje najviac zľava bit ako znakový bit. Preto je výsledok -2^7 + 2^6 + 2^3alebo -56.

Výsledok môže overiť rýchly test:

assertEquals (-56, b2);

Vidíme, že výpočty podpísaných a nepodpísaných čísel sú rovnaké. Rozdiely sa objavia, iba keď JVM interpretuje binárne znázornenie ako desatinné číslo.

Operácie sčítania, odčítania a násobenia môžu pracovať s nepodpísanými číslami bez potreby akýchkoľvek zmien v JDK. Ostatné operácie, napríklad porovnanie alebo rozdelenie, narábajú s podpísanými a nepodpísanými číslami odlišne.

To je miesto, kde vstupuje do hry Unsigned Integer API.

3. Nepodpísané celé číslo API

Unsigned Integer API poskytuje podporu pre celočíselnú aritmetiku bez znamienka v prostredí Java 8. Väčšina členov tohto API sú statické metódy v Celé číslo a Dlhé triedy.

Metódy v týchto triedach fungujú podobne. Zameriame sa teda na Celé číslo iba trieda, odchádzajúc z Dlhé trieda pre krátkosť.

3.1. Porovnanie

The Celé číslo trieda definuje metódu s názvom porovnať nepodpísané na porovnanie nepodpísaných čísel. Táto metóda považuje všetky binárne hodnoty za nepodpísané, ignoruje pojem signálny bit.

Začnime dvoma číslami na hranici znaku int Dátový typ:

int positive = Integer.MAX_VALUE; int negative = Integer.MIN_VALUE;

Ak porovnáme tieto čísla ako podpísané hodnoty, pozitívne je zjavne väčší ako negatívny:

int signedComparison = Integer.compare (kladné, záporné); assertEquals (1, signedComparison);

Pri porovnaní čísel ako nepodpísaných hodnôt sa za najvýznamnejší bit namiesto znakového bitu považuje bit najviac vľavo. Výsledok je teda iný, s pozitívne je menší ako negatívny:

int unsignedComparison = Integer.compareUnsigned (kladné, záporné); assertEquals (-1, unsignedComparison);

Malo by to byť jasnejšie, keď sa pozrieme na binárne znázornenie týchto čísel:

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

Keď je bit úplne zľava bit normálnej hodnoty, MIN_VALUE je o jednotku väčší ako MAX_VALUE v binárnej sústave. Tento test potvrdzuje, že:

assertEquals (záporné, kladné + 1);

3.2. Divízia a Modulo

Rovnako ako operácia porovnania, operácie s nepodpísaným delením a modulom spracúvajú všetky bity ako hodnotové bity. Kvocienty a zvyšky sa preto líšia, keď vykonávame tieto operácie na podpísaných a nepodpísaných číslach:

int positive = Integer.MAX_VALUE; int negative = Integer.MIN_VALUE; assertEquals (-1, negatívne / pozitívne); assertEquals (1, Integer.divideUnsigned (záporné, kladné)); assertEquals (-1, záporné% pozitívne); assertEquals (1, Integer.remainderUnsigned (záporné, kladné));

3.3. Analýza

Pri analýze a String pomocou parseUnsignedInt metóda, textový argument môže predstavovať číslo väčšie ako MAX_VALUE.

Veľkú hodnotu, ako je táto, nemožno analyzovať pomocou parseInt metóda, ktorá dokáže spracovať iba textové znázornenie čísel z MIN_VALUE do MAX_VALUE.

Nasledujúci testovací prípad overuje výsledky analýzy:

Throwable thrown = catchThrowable (() -> Integer.parseInt ("2147483648")); assertThat (vyhodené) .isInstanceOf (NumberFormatException.class); assertEquals (Integer.MAX_VALUE + 1, Integer.parseUnsignedInt ("2147483648"));

Všimnite si, že parseUnsignedInt metóda môže analyzovať reťazec označujúci číslo väčšie ako MAX_VALUE, ale nedokáže analyzovať žiadne negatívne zastúpenie.

3.4. Formátovanie

Podobne ako pri syntaktickej analýze, aj pri formátovaní čísla považuje nepodpísaná operácia všetky bity za hodnotové bity. V dôsledku toho môžeme vytvoriť textové znázornenie čísla, ktoré je asi dvakrát väčšie ako MAX_VALUE.

Nasledujúci testovací prípad potvrdzuje výsledok formátovania súboru MIN_VALUE v obidvoch prípadoch - podpísané a nepodpísané:

Reťazec signedString = Integer.toString (Integer.MIN_VALUE); assertEquals ("- 2147483648", signedString); Reťazec unsignedString = Integer.toUnsignedString (Integer.MIN_VALUE); assertEquals ("2147483648", unsignedString);

4. Klady a zápory

Mnoho vývojárov, najmä tých, ktorí pochádzajú z jazyka, ktorý podporuje nepodpísané dátové typy, ako je napríklad C, víta zavedenie nepodpísaných aritmetických operácií. Avšak to nemusí byť nevyhnutne dobrá vec.

Existujú dva hlavné dôvody dopytu po nepodpísaných číslach.

Po prvé, existujú prípady, pre ktoré nikdy nemôže dôjsť k zápornej hodnote, a použitie nepodpísaného typu môže takejto hodnote na prvom mieste zabrániť. Po druhé, s nepodpísaným typom môžeme zdvojnásobiť rozsah použiteľných pozitívnych hodnôt v porovnaní s jeho podpísaným náprotivkom.

Poďme analyzovať dôvody, ktoré stoja za odvolaním pre nepodpísané čísla.

Ak má byť premenná vždy nezáporná, hodnota menšia ako 0 môže byť užitočné pri označovaní výnimočnej situácie.

Napríklad String.indexOf metóda vráti pozíciu prvého výskytu určitého znaku v reťazci. Index -1 môže ľahko označiť absenciu takého znaku.

Ďalším dôvodom nepodpísaných čísel je rozšírenie hodnotového priestoru. Avšak ak rozsah podpísaného typu nestačí, je nepravdepodobné, že by stačil dvojnásobný rozsah.

V prípade, že dátový typ nie je dostatočne veľký, musíme použiť iný dátový typ, ktorý podporuje oveľa väčšie hodnoty, napríklad using dlho namiesto intalebo BigInteger radšej než dlho.

Ďalším problémom rozhrania Unsigned Integer API je to, že binárna forma čísla je rovnaká bez ohľadu na to, či je podpísané alebo nepodpísané. Je to preto ľahko kombinovateľné podpísané a nepodpísané hodnoty, čo môže viesť k neočakávaným výsledkom.

5. Záver

Podpora nepodpísanej aritmetiky v jazyku Java prišla na žiadosť mnohých ľudí. Výhody, ktoré prináša, sú nejasné. Pri používaní tejto novej funkcie by sme mali byť opatrní, aby sme sa vyhli neočakávaným výsledkom.

Zdrojový kód tohto článku je ako vždy k dispozícii na serveri GitHub.


$config[zx-auto] not found$config[zx-overlay] not found