Java 8 usigneret aritmetisk support

1. Oversigt

Fra begyndelsen af ​​Java er alle numeriske datatyper underskrevet. I mange situationer er det dog nødvendigt at bruge usignerede værdier. Hvis vi f.eks. Tæller antallet af forekomster af en begivenhed, vil vi ikke støde på en negativ værdi.

Støtten til usigneret aritmetik har endelig været en del af JDK fra version 8. Denne support kom i form af Unsigned Integer API, der primært indeholder statiske metoder i Heltal og Lang klasser.

I denne vejledning gennemgår vi denne API og giver instruktioner om, hvordan man bruger usignerede numre korrekt.

2. Bit-niveau repræsentationer

For at forstå, hvordan man håndterer underskrevne og usignerede numre, lad os først se på deres repræsentation på bitniveauet.

I Java kodes tal ved hjælp af de to komplement-system. Denne kodning implementerer mange grundlæggende aritmetiske operationer, herunder addition, subtraktion og multiplikation på samme måde, uanset om operanderne er signeret eller ikke signeret.

Ting skal være klarere med et kodeeksempel. Af hensyn til enkelheden bruger vi variabler af byte primitiv datatype. Operationer er ens for andre integrerede numeriske typer, f.eks kort, int, eller lang.

Antag, at vi har en eller anden type byte med værdien af 100. Dette tal har den binære repræsentation 0110_0100.

Lad os fordoble denne værdi:

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

Venstre skiftoperatør i den givne kode flytter alle bits i variabel b1 en position til venstre, hvilket teknisk set gør sin værdi dobbelt så stor. Den binære repræsentation af variabel b2 vil så være 1100_1000.

I et usigneret typesystem repræsenterer denne værdi et decimaltal svarende til 2^7 + 2^6 + 2^3, eller 200. Alligevel, i et signeret system fungerer den længst til venstre bit som tegnbit. Derfor er resultatet -2^7 + 2^6 + 2^3, eller -56.

En hurtig test kan verificere resultatet:

assertEquals (-56, b2);

Vi kan se, at beregningerne af underskrevne og usignerede numre er de samme. Forskelle vises kun, når JVM fortolker en binær repræsentation som et decimaltal.

Tilføjelses-, subtraktions- og multiplikationsoperationerne kan fungere med usignerede tal uden at kræve ændringer i JDK. Andre operationer, såsom sammenligning eller opdeling, håndterer underskrevne og usignerede numre forskelligt.

Det er her, Unsigned Integer API kommer i spil.

3. Unsigned Integer API

Unsigned Integer API understøtter usigneret heltal-aritmetik i Java 8. De fleste medlemmer af denne API er statiske metoder i Heltal og Lang klasser.

Metoder i disse klasser fungerer på samme måde. Vi vil således fokusere på Heltal kun klasse, uden at Lang klasse for kortfattethed.

3.1. Sammenligning

Det Heltal klasse definerer en metode, der hedder sammenligneUnsigneret for at sammenligne usignerede numre. Denne metode betragter alle binære værdier som usignerede og ignorerer forestillingen om tegnbit.

Lad os starte med to tal ved grænserne for int datatype:

int positiv = Integer.MAX_VALUE; int negativ = Heltal.MIN_VALUE;

Hvis vi sammenligner disse tal som underskrevne værdier, positiv er tydeligvis større end negativ:

int signeretComparison = Integer.compare (positiv, negativ); assertEquals (1, underskrevet sammenligning);

Når man sammenligner tal som usignerede værdier, betragtes den længst til venstre bit som den mest betydningsfulde bit i stedet for tegnbiten. Resultatet er således anderledes med positiv er mindre end negativ:

int unsignedComparison = Integer.compareUnsigned (positiv, negativ); assertEquals (-1, unsignedComparison);

Det burde være klarere, hvis vi ser på den binære repræsentation af disse tal:

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

Når den venstre bit er en almindelig værdi bit, MIN_VALUE er en enhed større end MAX_VALUE i det binære system. Denne test bekræfter, at:

assertEquals (negativ, positiv + 1);

3.2. Division og Modulo

Ligesom sammenligningsoperationen, de usignerede delings- og modulo-operationer behandler alle bits som værdibit. Kvotienter og resterende er derfor forskellige, når vi udfører disse operationer på underskrevne og usignerede numre:

int positiv = Integer.MAX_VALUE; int negativ = Heltal.MIN_VALUE; assertEquals (-1, negativ / positiv); assertEquals (1, Integer.divideUnsigned (negativ, positiv)); assertEquals (-1, negativ% positiv); assertEquals (1, Integer.remainderUnsigned (negativ, positiv));

3.3. Analyse

Ved analyse af en Snor bruger parseUnsignedInt metode, tekstargumentet kan repræsentere et tal større end MAX_VALUE.

En sådan stor værdi kan ikke analyseres med parseInt metode, som kun kan håndtere tekstlig repræsentation af tal fra MIN_VALUE til MAX_VALUE.

Følgende testtilfælde verificerer parseringsresultaterne:

Kastbar kastet = catchThrowable (() -> Integer.parseInt ("2147483648")); assertThat (kastet) .isInstanceOf (NumberFormatException.class); assertEquals (Integer.MAX_VALUE + 1, Integer.parseUnsignedInt ("2147483648"));

Bemærk, at parseUnsignedInt metoden kan analysere en streng, der indikerer et tal større end MAX_VALUE, men undlader at analysere nogen negativ repræsentation.

3.4. Formatering

Svarende til parsing betragter en usigneret operation, når der formateres et tal, alle bits som værdi-bits. Følgelig, vi kan producere den tekstlige repræsentation af et tal, der er cirka dobbelt så stort som MAX_VALUE.

Følgende testsag bekræfter formateringsresultatet af MIN_VALUE i begge tilfælde - underskrevet og usigneret:

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

4. Fordele og ulemper

Mange udviklere, især dem der kommer fra et sprog, der understøtter usignerede datatyper, såsom C, glæder sig over introduktionen af ​​usignerede aritmetiske operationer. Imidlertid, dette er ikke nødvendigvis en god ting.

Der er to hovedårsager til kravet om usignerede numre.

For det første er der tilfælde, hvor en negativ værdi aldrig kan forekomme, og brug af en usigneret type kan forhindre en sådan værdi i første omgang. For det andet med en usigneret type kan vi fordoble rækkevidden af ​​anvendelige positive værdier sammenlignet med dets underskrevne modstykke.

Lad os analysere begrundelsen bag appellen om usignerede numre.

Når en variabel altid skal være ikke-negativ, er en værdi mindre end 0 kan være praktisk til at angive en usædvanlig situation.

For eksempel String.indexOf metode returnerer positionen for den første forekomst af et bestemt tegn i en streng. Indekset -1 kan let betegne fraværet af en sådan karakter.

Den anden årsag til usignerede tal er udvidelsen af ​​værdirummet. Imidlertid, hvis rækkevidden for en signeret type ikke er nok, er det usandsynligt, at en fordoblet rækkevidde er tilstrækkelig.

Hvis en datatype ikke er stor nok, skal vi bruge en anden datatype, der understøtter meget større værdier, f.eks. Ved hjælp af lang i stedet for int, eller BigInteger hellere end lang.

Et andet problem med Unsigned Integer API er, at den binære form for et nummer er den samme, uanset om den er signeret eller usigneret. Det er derfor let at blande signerede og usignerede værdier, hvilket kan føre til uventede resultater.

5. Konklusion

Støtten til usigneret aritmetik i Java er kommet efter anmodning fra mange mennesker. De fordele, det medfører, er imidlertid uklare. Vi skal udvise forsigtighed, når vi bruger denne nye funktion for at undgå uventede resultater.

Som altid er kildekoden til denne artikel tilgængelig på GitHub.