Inhoudsopgave:
Video: The mind behind Linux | Linus Torvalds 2024
Geloof het of niet, computers - zelfs de krachtigste - hebben bepaalde beperkingen als het gaat om het uitvoeren van wiskundige berekeningen. Deze beperkingen zijn meestal onbelangrijk, maar soms sluipen ze op en bijten ze je. Dit zijn de dingen waar je op moet letten als je in Java wiskunde doet.
Integer-overloop
Het basisprobleem met integer-typen is dat ze een vaste grootte hebben. Als gevolg hiervan is er een limiet voor de grootte van de getallen die kunnen worden opgeslagen in variabelen van het type
kort
,
int
of
lang
. Hoewel
lange
variabelen grote getallen kunnen bevatten, kom je vroeg of laat een getal tegen dat te groot is om in een
lange
variabele te passen.
Oké, overweeg dit (weliswaar gekunstelde) voorbeeld:
int a = 1000000000;
Systeem. uit. println (a);
a + = 1000000000;
Systeem. uit. println (a);
a + = 1000000000;
Systeem. uit. println (a);
a + = 1000000000;
Systeem. uit. println (a);
Hier verwacht u dat de waarde van
a
na elke toevoeging groter wordt. Maar hier is de uitvoer die wordt weergegeven:
1000000000
2000000000
-1294967296
-294967296
De eerste toevoeging lijkt te werken, maar daarna wordt het aantal negatief! Dat komt omdat de waarde de maximale grootte van het gegevenstype
int
heeft bereikt. Helaas vertelt Java niet dat deze fout is opgetreden. Het crams gewoon de
int
variabele zo vol met bits als het kan, verwijdert alle bits die niet passen, en hoopt dat je het niet merkt. Vanwege de manier waarop
int
negatieve waarden opslaat, worden grote positieve waarden plotseling grote negatieve waarden.
De moraal van het verhaal is dat als u met grote gehele getallen werkt, u
lang
moet gebruiken in plaats van
int
, omdat
lang is
kan veel grotere aantallen opslaan dan
int
. Als uw programma's omgaan met getallen die groot genoeg zijn om een probleem te zijn voor
lang
, overweeg dan drijvende-kommatypes te gebruiken. Drijvende-kommatypes kunnen zelfs grotere waarden verwerken dan
lang
, en ze laten u weten wanneer u hun capaciteit overschrijdt.
Floating-point weirdness
Floating-point-nummers hebben eigen problemen. Om te beginnen worden drijvende-kommawaarden opgeslagen met behulp van het binaire getalsysteem (basis 2), maar mensen werken met getallen in het decimale getalsysteem (basis 10). Helaas is het soms onmogelijk om getallen tussen deze twee systemen nauwkeurig te converteren. Dat komt omdat in elke nummerbasis bepaalde breuken niet exact kunnen worden weergegeven.
Een voorbeeld: Base 10 kan de fractie 1/3 niet exact weergeven. Je kunt het bij benadering als 0.3333333 benaderen, maar uiteindelijk bereik je de limiet van het aantal cijfers dat je kunt opslaan, dus je moet stoppen. In basis 2 gebeurt het dat een van de breuken die u niet nauwkeurig kunt weergeven, de decimale waarde 1/10 is. Met andere woorden, een
float
of
dubbele
variabele kan
0 niet nauwkeurig weergeven. 1
.
Probeer deze code uit te voeren:
float x = 0. 1f;
NumberFormat nf = NumberFormat. getNumberInstance ();
nf. setMinimumFractionDigits (10);
Systeem. uit. println (nf.formaat (x));
De resulterende uitvoer is deze:
0. 1000000015
Hoewel
0. 1000000015
is in de buurt tot
0. 1
, het is niet exact.
In de meeste gevallen is de floating-point-wiskunde van Java zo dichtbij dat het er niet toe doet. De foutenmarge is extreem klein. Als u Java gebruikt om de grootte van uw huis te meten, hebt u een elektronenmicroscoop nodig om de fout op te merken. Als u echter toepassingen schrijft die met financiële transacties te maken hebben, kan normale afronding de fouten soms vergroten om ze aanzienlijk te maken. U mag een cent te veel of te weinig omzetbelasting vragen. En in extreme gevallen hebben uw facturen mogelijk duidelijke uitbreidingsfouten.
Geheel getaltypen worden natuurlijk ook in binair formaat opgeslagen. Maar gehele getallen zijn niet onderhevig aan dezelfde fouten die drijvende-kommatypes zijn - omdat gehele getallen helemaal geen breuken vertegenwoordigen - dus u hoeft zich geen zorgen te maken over dit type fout voor
integer
-typen.
Verdeling met nul
Volgens de basisregels van de wiskunde kunt u een getal niet met nul delen. De reden is simpel: delen is het omgekeerde van vermenigvuldiging - wat betekent dat als
a * b = c
, het ook waar is dat
a = c / b
. Als u
b
zou toestaan nul te zijn, zou deling zinloos zijn, omdat elk aantal keer nul nul is. Daarom moeten zowel
a
en
c
ook nul zijn. Kortom, wiskundigen hebben dit dilemma eeuwen geleden opgelost door te zeggen dat deling door nul simpelweg niet is toegestaan.
Dus wat gebeurt er als u doet probeert een getal door nul te delen in een Java-programma? Het antwoord hangt af van het feit of u hele getallen of drijvende-kommagetallen deelt. Als u gehele getallen deelt, verspert de instructie die de deling door nul probeert, wat een -uitzondering wordt genoemd,, wat een onbeleefde manier is om het programma te laten crashen.
Er is een manier om deze uitzondering te onderscheppen zodat uw programma kan doorgaan, wat u hier niet kunt vinden. In de tussentijd, elk programma dat je schrijft dat een integer deling door nul crasht probeert.
Als u een floating-point-type met nul wilt delen, zijn de resultaten niet zo abrupt. In plaats daarvan wijst Java het drijvende-kommaresultaat toe aan een van de speciale waarden in de onderstaande tabel. In de volgende paragrafen wordt uitgelegd hoe deze speciale waarden worden bepaald:
- Als u een getal door nul deelt en het teken van beide getallen hetzelfde is, is het resultaat positief oneindig.
0. 0
gedeeld door0. 0
is positief oneindig, net als-34. 0
gedeeld door-0. 0
. - Als u een getal door nul deelt en de tekens van de cijfers verschillen, is het resultaat negatief oneindig.
-40. 0
gedeeld door0. 0
is een negatieve oneindigheid, zoals34. 0
gedeeld door0. 0
. - Als u nul door nul deelt, is het resultaat geen getal (NaN), ongeacht de tekens.
Constant | Betekenis |
POSITIVE_INFINITY
|
positieve oneindig |
NEGATIVE_INFINITY
|
negatieve oneindigheid |
NaN
|
geen getal |
Drijvende nulpunten kunnen positief of negatief zijn. Java vindt dat positieve en negatieve nullen numeriek gelijk zijn.
Als u probeert een drijvende-kommawaarde met een van deze speciale waarden af te drukken, converteert Java de waarde naar een geschikte tekenreeks. Stel dat u de volgende instructies uitvoert:
double x = Math. sqrt (-50); // Niet een getal
dubbel y = x;
if (x == y)
Systeem. uit. println ("x is gelijk aan y");
De resulterende consoleopdracht is
Infinity
If
i
was
-50. 0
, geeft de console
-Infinity
weer en als
i
nul was, zou de console
NaN
weergeven.
De volgende paragrafen beschrijven een aantal laatste stukjes raarheid:
-
NaN
is niet gelijk aan zichzelf, wat een paar vreemde gevolgen kan hebben. Bijvoorbeeld:
dubbel x = wiskunde. sqrt (-50); // Niet een getal
dubbel y = x;
if (x == y)
Systeem. uit. println ("x is gelijk aan y");
Neem voor de goede redenering aan dat de
if
-instructie test of de variabele
x
gelijk is aan de variabele
y
. Omdat deze test onmiddellijk volgt op een toewijzingsinstructie die de waarde van
x
toekent aan
y
, kunt u gerust aannemen dat
x
gelijk is aan
y
, rechts?
Wrong. Omdat
x
NaN
is, is
y
ook
NaN
.
NaN
wordt nooit beschouwd als gelijk aan enige andere waarde, waaronder een andere
NaN
. Dus de vergelijking in de
if
-instructie mislukt.
- Nog een vreemde consequentie: je kunt niet aannemen dat een getal minus zelf altijd nul is. Overweeg deze uitspraak:
dubbel z = x - x; // niet noodzakelijkerwijs nul
Zou deze uitspraak nooit
z
op nul moeten zetten? Niet als
x
NaN
is. In dat geval is geen nummer minus geen nummer nog steeds geen cijfer.
- Nog een rare: elke wiskundige bewerking met oneindigheid resulteert in een andere oneindigheid of
NaN
. Infinity + 5, bijvoorbeeld, is nog steeds gelijk aan oneindig, dus de oproep van Buzz Lightyear: "Naar het oneindige en verder! "Het gaat gewoon niet gebeuren. Maar oneindig minus oneindig geeft je …NaN
.