1. Reële getallen afronden

Zoals we al hebben besproken, wordt bij het toekennen van een reëel getal aan een intvariabele altijd naar beneden afgerond naar het dichtstbijzijnde kleinere gehele getal — het breukdeel wordt gewoon weggegooid.

Maar het is gemakkelijk om je een situatie voor te stellen waarin een gebroken getal moet worden afgerond naar het dichtstbijzijnde gehele getal in beide richtingen of zelfs naar boven moet worden afgerond. Wat doe je in dit geval?

Voor deze en voor veel vergelijkbare situaties heeft Java de Mathklasse, die de methoden round(), ceil(), en heeft floor().


Math.round()methode

De Math.round()methode rondt een getal af op het dichtstbijzijnde gehele getal:

long x = Math.round(real_number)

Maar er is nog een andere nuance: deze methode retourneert een longgeheel getal (geen int). Omdat reële getallen erg groot kunnen zijn, besloten de makers van Java Java's grootste beschikbare gehele type te gebruiken: long.

Dienovereenkomstig, als een programmeur het resultaat aan een variabele wil toewijzen int, moet hij de compiler expliciet aangeven dat hij het mogelijke verlies van gegevens accepteert (in het geval dat het resulterende getal niet in een inttype past).

int x = (int) Math.round(real_number)

Voorbeelden:

Stelling Resultaat
int x = (int) Math.round(4.1);
4
int x = (int) Math.round(4.5);
5
int x = (int) Math.round(4.9);
5

Math.ceil()methode

De Math.ceil()methode rondt een getal naar boven af ​​op een geheel getal. Hier zijn voorbeelden:

Stelling Resultaat
int x = (int) Math.ceil(4.1);
5
int x = (int) Math.ceil(4.5);
5
int x = (int) Math.ceil(4.9);
5

Math.floor()methode

De Math.floor()methode rondt een getal naar beneden af ​​op een geheel getal. Hier zijn voorbeelden:

Stelling Resultaat
int x = (int) Math.floor(4.1);
4
int x = (int) Math.floor(4.5);
4
int x = (int) Math.floor(4.9);
4

Bij het afronden van een getal naar een geheel getal is het natuurlijk gemakkelijker om gewoon een type cast-operator te gebruiken:(int)

Stelling Resultaat
int x = (int) 4.9
4

Als je het moeilijk vindt om deze namen te onthouden, kan een korte Engelse les helpen:

  • Mathbetekent wiskunde
  • Roundbetekent rond
  • Ceilingbetekent plafond
  • Floorbetekent vloer

2. Hoe getallen met drijvende komma zijn gestructureerd

Het doubletype kan waarden opslaan in het bereik van tot . Dit enorme bereik aan waarden (vergeleken met het type) wordt verklaard door het feit dat het type (evenals ) een geheel andere interne structuur heeft dan integer-typen. Intern codeert het type zijn waarde als twee getallen: de eerste wordt de mantisse genoemd en de tweede wordt de exponent genoemd .-1.7*10308+1.7*10308intdoublefloatdouble

Laten we zeggen dat we het nummer hebben 123456789en het opslaan als een doublevariabele. Wanneer we dat doen, wordt het nummer geconverteerd naar , en intern slaat het type twee nummers op - en . De significand ("significant deel van het getal" of mantisse) is rood gemarkeerd, terwijl de exponent blauw is gemarkeerd.1.23456789*108double234567898

Deze aanpak maakt het mogelijk om zowel zeer grote als zeer kleine aantallen op te slaan. Maar omdat de weergave van het getal beperkt is tot 8 bytes (64 bits) en sommige bits worden gebruikt om de exponent op te slaan (evenals het teken van de mantisse en het teken van de exponent), zijn de maximale beschikbare cijfers om de mantisse weer te geven is 15 .

Dit is een zeer vereenvoudigde beschrijving van hoe reële getallen zijn gestructureerd.


3. Verlies van precisie bij het werken met reële getallen

Houd er bij het werken met reële getallen altijd rekening mee dat reële getallen niet exact zijn . Bij het omzetten van decimaal naar binair kunnen er altijd afrondings- en conversiefouten optreden . Bovendien is de meest voorkomende foutbron het verlies van precisie bij het optellen/aftrekken van getallen op radicaal verschillende schalen.

Dit laatste feit is een beetje verbluffend voor beginnende programmeurs.

Als we aftrekken van , krijgen we .1/109109109

Getallen aftrekken op radicaal verschillende schalen Uitleg
 1000000000.000000000;
-         0.000000001;
 1000000000.000000000;
Het tweede getal is extreem klein , waardoor de significantie (grijs gemarkeerd) wordt genegeerd. De 15 significante cijfers zijn oranje gemarkeerd.

Wat kunnen we zeggen, programmeren is niet hetzelfde als wiskunde.


4. Valkuil bij het vergelijken van reële getallen

Een ander gevaar ligt op de loer voor programmeurs wanneer ze echte getallen vergelijken. Het ontstaat bij het werken met reële getallen, omdat afrondingsfouten zich kunnen ophopen. Het resultaat is dat er situaties zijn waarin wordt verwacht dat reële getallen gelijk zijn, maar dat is niet het geval. Of vice versa: de cijfers zijn naar verwachting verschillend, maar ze zijn gelijk.

Voorbeeld:

Stelling Uitleg
double a = 1000000000.0;
double b = 0.000000001;
double c = a - b;
De waarde van de variabele a zal zijn 1000000000.0
De waarde van de variabele c zal zijn 1000000000.0
(het getal in de b variabele is te klein)

In bovenstaand voorbeeld zouden aen cniet gelijk moeten zijn, maar dat zijn ze wel.

Of laten we een ander voorbeeld nemen:

Stelling Uitleg
double a = 1.00000000000000001;
double b = 1.00000000000000002;
De waarde van de variabele a zal zijn 1.0
De waarde van de variabele b zal zijn1.0

5. Een interessant feit overstrictfp

Java heeft een speciaal trefwoordstrictfp ( strict floating point ) , dat niet voorkomt in andere programmeertalen. En weet je waarom je het nodig hebt? Het verslechtert de nauwkeurigheid van bewerkingen met getallen met drijvende komma. Hier is het verhaal van hoe het tot stand kwam:

Java's makers:
We willen echt dat Java super populair wordt en dat Java-programma's op zoveel mogelijk apparaten worden uitgevoerd. Dus hebben we ervoor gezorgd dat de specificatie voor de Java-machine zegt dat alle programma's op alle soorten apparaten op dezelfde manier moeten werken!
Makers van Intel-processors:
Dag Allemaal! We hebben onze processors verbeterd en nu worden alle reële getallen weergegeven met behulp van 10 bytes in plaats van 8 bytes in onze processors. Meer bytes betekent meer significante cijfers. Wat betekent dat? Dat is juist! Nu zullen uw wetenschappelijke berekeningen nog nauwkeuriger zijn!
Wetenschappers en iedereen die betrokken is bij ultraprecieze berekeningen:
Koel! Goed gedaan. Geweldig nieuws!
Java's makers:
Nee-nee-nee, jongens! We vertelden je al dat alle Java-programma's op alle apparaten hetzelfde moeten draaien . We gaan met geweld de mogelijkheid uitschakelen om reële getallen van 10 bytes te gebruiken in Intel-processors.
Nu is alles weer in orde! Bedank ons ​​niet.
Wetenschappers en iedereen die betrokken is bij ultraprecieze berekeningen:
Ben je helemaal gek geworden? Snel alles terug zoals het was!
Java's makers:
Jongens, dit is voor je eigen bestwil! Stelt u zich eens voor: alle Java-programma's werken op alle apparaten op dezelfde manier . Dat is zo cool!
Wetenschappers en iedereen die betrokken is bij ultraprecieze berekeningen:
Nee. Het is helemaal niet cool. Snel alles terugzetten zoals het was! Of weet u waar we uw Java zullen plaatsen?
Java's makers:
Hm. Waarom zei je dat niet meteen? Natuurlijk zetten we het terug.
We hebben uw mogelijkheid hersteld om alle functies van de nieuwste processors te gebruiken.
Trouwens... We hebben ook speciaal het strictfptrefwoord aan de taal toegevoegd. Als u het voor de naam van een functie schrijft, zijn alle bewerkingen met reële getallen binnen die functie even slecht op alle apparaten !