| Auf dieser Seite werden einige spezielle Funktionen an der Grenze zwischen Gleitkomma-Zahlen und Ganzen Zahlen vorgestellt. Obwohl oft verwendet, sind sie erstaunlich unscharf definiert. | Die benötigten Funktionen sind zwar in allen gängigen Programmiersprachen enthalten, werden jedoch teilweise unterschiedlich angewendet. Alle Beispiele ohne Gewähr - Die Anwendung erfolgt auf eigenes Risiko ! |
Algorithmen |
Ausgewählte IT-Rezepte |
| Abschneiden | Abschneiden von Gleitkomma-Zahlen zu ganzen Zahlen: ceil(), floor(), int() - Details |
| Runden | Runden auf Ganze Zahlen oder eine Anzahl Dezimalstellen - Details |
| Modulo | Der Rest nach ganzzahliger Division - Details |
| Große Zahlen | Spezielle Probleme großer ganzer Zahlen |
| Primzahlen | Unteilbare Zahlen |
Abschneide-Funktionen ceil(), floor(), int() |
|
|
Die Umwandlung von Gleitkomma-Zahlen in ganze Zahlen kann so erfolgen,
dass man den Nachkomma-Rest abschneidet. Diese Anweisung klingt einfach, tatsächlich gibt es jedoch 3 verschiedene Möglichkeiten, das zu tun. |
In allen 3 Versionen wird die Richtung vorgegeben,
in welcher die nächste ganze Zahl zu suchen ist. Damit unterscheidet sich → Abschneiden von → Runden: Beim Runden wird unabhängig von der Richtung die nächst-liegende ganze Zahl gesucht. • Oft interessiert nicht nur die von der Abschneide-Funktion berechnete ganze Zahl sondern auch der abgeschnittene Rest: Ein Spezialfall ist die ↓ Modulo-Funktion. |
Funktion ceil()gibt in jedem Fall die nächst-größere ganze Zahl zurück.• Von negativen Argumenten x<0 wird der Nachkomma-Rest abgeschnitten. • Für positive Argumente x>0 wird der Nachkomma-Rest formal vergrößert, bis er bei der nächst-größeren ganzen Zahl wieder =0 wird. |
Alle modernen Programmiersprachen bieten diese Funktion. Auch in jenen Programmen und Programmiersprachen, die keine ceil()-Funktion bieten, lässt sich die Funktion selbst erstellen. ♦ Details zur Funktion ceil() |
Funktion floor()gibt in jedem Fall die nächst-kleinere ganze Zahl zurück.• Von positiven Argumenten x<0 wird der Nachkomma-Rest abgeschnitten. • Für negative Argumente x>0 wird der (absolute) Nachkomma-Rest vergrößert, bis er bei der nächst-kleineren ganzen Zahl wieder =0 wird. |
Alle modernen Programmiersprachen bieten diese Funktion. Auch in jenen Programmen und Programmiersprachen, die keine floor()-Funktion bieten, lässt sich die Funktion selbst erstellen. ♦ Details zur Funktion floor() |
Funktion int()Diese Funktion ist leider nicht eindeutig definiert. Das Verhalten ist je nach Programm bzw. Programmiersprache unterschiedlich. |
Auf der Seite → Abschneide-Funktionen finden sie Details zu dieser Funktion. |
Symmetrische Abschneide-FunktionDiese Funktion schneidet als einzige den Nachkomma-Rest beliebiger Argumente ab.• Diese Funktion wirkt auf positive Argumente x>0 so wie floor() • Sie wirkt auf negative Argumente x<0 so wie ceil() |
Diese Funktion hat keinen eindeutigen Namen. Wenn sie von Programmen oder Programmiersprachen nicht angeboten wird, dann kann man sie selbst erstellen. ♦ Details zur symmetrischen Abschneide-Funktion |
Runden von Gleitkomma-Zahlen |
|
| Die Umwandlung von Gleitkomma-Zahlen in ganze Zahlen kann so erfolgen, dass man die nächst-liegende ganze Zahl sucht. |
In der Praxis gibt es jedoch mehrere Möglichkeiten, um Gleitkomma-Zahlen zu runden. Das entscheidende Kriterium ist immer die kleinste mögliche Entfernung zum Ergebnis, unabhängig von der Richtung. |
Runden auf ganze ZahlenIn diesem einfachsten Fall wird stets die nächst-liegende ganze Zahl gesucht. |
Alle gängigen Programme und Programmiersprachen bieten diese → Rundungs-Variante. |
Runden auf Dezimal-StellenMan kann vorgeben, das Ergebnis einer Rundung auf eine bestimmte Anzahl von Nachkomma-Stellen zu begrenzen. |
Die häufigste Variante ist die → kommerzielle Rundung auf 2 Nachkomma-Stellen, z.B. eines Geld-Betrags auf 0.01 € |
Runden auf größere EinheitenManche Rundungs-Funktionen erlauben die Angabe von <0 Nachkomma-Stellen. |
In diesem Fall wird auf ganze Zehner, Hunderter, Tausender... gerundet. |
Runden auf End-ZiffernIm kommerziellen Bereich wird oft auf bestimmte End-Ziffern gerundet, z.B. auf 0.0, 0.2, 0.4, 0.6, 0.8 oder nur auf 0.0 und 0.5 (€). |
Diese Funktion muss man in jedem Fall selbst programmieren (♦ Details) |
Runden auf signifikante ZiffernLeider nur in seltenen Fällen werden Funktionen zum relativen Runden geboten: In diesem Fall ist der in Kauf genommene Rundungs-Fehler nicht absolut festgelegt, sondern vom jeweiligen Wert abhängig. |
Im einfachsten Fall wird die Genauigkeit der Rundung durch die Anzahl signifikanter Ziffern vorgegeben. |
| ♦ Details der Rundungs-Funktionen: Auf dieser Seite werden sowohl die angebotenen Rundungs-Funktionen vorgestellt, als auch selbst programmierte Funktionen als Ergänzung. | |
Modulo-Funktion (Divisions-Rest) |
|
|
Diese Funktion liefert im einfachsten Fall den 'Rest einer ganzzahligen Division'. Das Ergebnis ist in der Praxis nur für positive ganze Zahlen eindeutig. |
Für negative und Gleitkomma-Zahlen ist das Verrhalten dieser Funktion nicht eindeutig, d.h. es gibt in der Praxis unterschiedliche Versionen. |
| ♦ Details der Modulo-Funktion: Auf dieser Seite werden die gebotenen Versionen der Modulo-Funktion vorgestellt, und ebenso selbst programmierte Funktionen zur Ergänzung allenfalls fehlender Versionen. | |
Große ganze Zahlen |
|||||||||||||||||||||||||||||||||||
Werte-BereichGanze Zahlen werden normalerweise binär gespeichert. Aus der Größe ('Wortbreite') b der Speicher-Zellen ergibt sich daher die Obergrenze imax für den Wert positiver ganzer Zahlen (unsigned)unsigned: imin=0; imax=2^(b-1);
Negative Zahlen (signed) werden im
→
2er-Komplement gespeichert: Das höchst-wertige Bit wird als Vorzeichen verwendet.
signed: imin=-2^(b-1); imax=2^(b-1)-1;
In der Praxis ist die verwendete Wortbreite nur selten bekannt, meist auch
abhängig von Betriebssystem und Version der Software.
|
|
||||||||||||||||||||||||||||||||||
ÜberschreitungWenn bei der Zuweisung von Werten der zulässige Bereich (Tabelle rechts oben) überschritten wird, dann kann die Software unterschiedlich reagieren:● (Automatische) Umwandlung in eine Gleitkomma-Zahl. Das erlaubt die weitere Arbeit ohne Fehler-Meldung, verringert jedoch die Genauigkeit. Die meisten AnwenderInnen-Programme und viele moderne Programmiersprachen reagieren so. Die → Genauigkeit von → Gleitkomma-Zahlen beträgt derzeit je nach Software meist ~16 Dezimalstellen. Bei dieser Dimension sind aufeinander folgende ganze Zahlen nicht mehr unterscheidbar: 1E+17 + 1 = 1E17 ● Keine Reaktion. In diesem Fall ergeben sich ohne Warnung Rechenfehler, z.B. bei einem 'wrap-around' 32767 + 1 = -32768
So reagieren die meisten Programmiersprachen, wenn der verwendete Variablen-Typ
ausdrücklich vereinbart wurde.● Fehler-Meldung und Abbruch des Programms. |
▼ Keine der möglichen Reaktionen ist wünschenswert. Besonders gefährlich ist die Möglichkeit, dass ohne Warnung Rechenfehler auftreten können. Testen sie daher unbedingt das Verhalten ihrer Software beim Überschreiten der Ganzzahlen-Grenzen ! Ein Ersatz ganzer Zahlen durch Gleitkomma-Zahlen ist in manchen Fällen (Schleifen-Variable) nicht möglich und meist wegen der wesentlich langsameren Verarbeitung auch nicht wünschenswert. Alle modernen Programmiersprachen bieten eigene Module zur Verarbeitung großer Zahlen, insbesondere auch von großen ganzen Zahlen. ♣ Tipp: verwenden sie diese Module, wenn Verdacht besteht, dass die von ihnen verwendeten ganzen Zahlen die Grenze überschreiten und ohne Warnung automatisch in Gleitkomma-Zahlen umgewandelt werden. Diese Module rechnen wesentlich langsamer, jedoch auch mit beliebig großen ganzen Zahlen exakt. PHP: eines der beiden → PHP-Module BC-Math oder GMP ! Perl: das → Perl-Modul Math::BigInt ! |
||||||||||||||||||||||||||||||||||
► Kalkulation (LibreOffice, MS-Excel):Es werden Gleitkomma-Zahlen verwendet. Beachten sie daher die Grenzen der Genauigkeit, z.B.
A1 = 2
In Zelle D1 wird zunächst korrekt berechnet
B1 = 10^A1 C1 = B1+1 D1 = C1-B1 D1 = 10^2+1 - 10^2 = 101-100 = 1 Erhöhen sie schrittweise den Wert in A1 Die Berechnung stimmt bis A1=15 und versagt ab A1>=16 |
► Basic (LibreOffice-Basic, Visual Basic, VBA):Die Bereichs-Grenzen werden streng überwacht: Umwandlungs-Funktionen ergeben Fehler bei Überschreitung der zulässigen Grenzen (Gleitkomme-Argumente werden gerundet):
0..CByte..255, -32768..CInt..+32767, -2147483648..CDbl..+2147483647
Das gilt auch für implizite Typ-Umwandlung, z.B.
Function intest(x As Integer) As Integer
Diese Funktion arbeitet korrekt für -32768.499..x..-32766.499
und ergibt Fehler für kleinere oder größere Argumente.
Wenn man die Typ-Vereinbarung weglässt, dann wird Gleitkomma-Genauigkeit
verwendet und wesentlich langsamer gerechnet.
intest = x + 1
End Function
|
||||||||||||||||||||||||||||||||||
► PHP:Das → Modul bcmath ermöglicht (langsame) Rechnungen mit beliebiger Genauigkeit, da die Zahlen intern als Strings verwaltet werden. (PHP-Manual)Das Modul gmp bietet ähnliche Möglichkeiten unter Verwendung der GNU Multi-Precision Library (PHP-Manual) GMP scheint flexibler und schneller zu sein, das hängt jedoch vermutlich von den jeweiligen Rechnungen ab. |
► Javascript:Bietet keine Standard-Möglichkeiten. Man findet im Internet (BigMath, Big Numbers) einige Funktions-Bibliotheken, die man jedoch selbst testen sollte (BigInt)Im Zweifel muss man die Zahlen in Strings oder Ziffern-Arrays umwandeln und selbst stellenweise rechnen. |
||||||||||||||||||||||||||||||||||
► PerlPerl bietet einstellbares Verhalten: Ohne besondere Vereinbarung wird immer Gleitkomma-Arithmetik verwendet, d.h. mit der erwähnten Beschränkung der Genauigkeit.
$a=2; $b=10**$a;
Dieses Beispiel funktioniert (ergibt $d=1 )
bis $a=15 und versagt bei größeren Zahlen.$c=$b+1; $d=$c-$b; Mit dem integer-Pragma (Compiler-Anweisung) wird Integer-Arithmetik erzwungen:
my $d=3.45;
In diesem Beispiel gilt das integer-Pragma (nur) innerhalb des
Blocks {} {
use integer;
}
my $id=4.56; my $ic=2*$d; # ic=6 print "d=$d, ic=$ic\n"; $ic=2*$id; # ic=8 print "d=$d, ic=$ic\n"; Gleitkomma-Variable $d behalten ihren Wert, lokale Variable $id lassen sich ebenfalls auf Gleitkomma-Werte setzen. Arithmetische Operationen liefern jedoch stets ganzzahlige Werte (für $ic ). Der Werte-Bereich (nur) für Ergebnisse von Rechnungen ist innerhalb des Blocks auf -2**31..+2**31-1 begrenzt.
use integer;
In diesem Beispiel wird die Obergrenze 2**31 mit der Variablen
$id überschritten. Beim Rechnen (Bitmuster) ist das kein Problem.
Bei der Ausgabe wird $id als
Zahl >2**31 behandelt, $ic
als negative Zahl.my($id,$ic,$ii); $id = 2**31-3; for($ii=0;$ii<10;$ii++) {
$ic = $id*2;
}
print "$id * 2 = $ic\n"; $id++; Bit-Operatoren interpretieren ganze Zahlen normalerweise als unsigned, unter integer-Pragma als signed, z.B. ~0 ergibt -1 -1 & -5 ergibt -6 ♦ Details zu Bit-Operatoren |
Die Pragmas (Compiler-Direktiven) bigint
(nur für ganze Zahlen) und bignum (auch für
Gleitkomma-Zahlen) erlauben die arithmetisch exakte Verwendung von praktisch
beliebig großen Zahlen. Die Zahlen werden in diesem Fall als Multibyte-Objekte gespeichert, die Rechen-Geschwindigkeit reduziert sich mit zunehmender Größe der Zahlen.
use bigint;
Dieses Beispiel berechnet die Differenz $ei zweier
großer ganzer Zahlen. Erhöhen sie den Schleifen-Zähler, z.B.
von 100..$bi..110 my($bi,$ci,$di,$ei); for($bi=0;$bi<10;$bi++) {
$ci = 2**$bi-1;
}
$di = $ci+1; $ei = $di-$ci; print "bi=$bi, ci=$ci, di=$di,$ei\n"; #print "bi=$bi, ei=$ei\n";
Das Ergebnis bleibt richtig. So große Zahlen sind kaum mehr auszugeben, aktivieren sie daher den zweiten print-Befehl und schalten sie den ersten ab. Auch bei 10000..$bi..10010 wird noch richtig gerechnet, wenn auch bereits merkbar langsamer. Allerdings ist 2**10000 = 10**(10000*ln(2)/ln(10)) eine unvorstellbar große ganze Zahl mit >3000 Dezimal-Ziffern. |
||||||||||||||||||||||||||||||||||