Ganzzahlen-Funktionen

Abschneiden, Runden, Rest, Primzahlen

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-Funktion

Diese 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 Zahlen

In 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-Stellen

Man 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 Einheiten

Manche Rundungs-Funktionen erlauben die Angabe von <0 Nachkomma-Stellen.

In diesem Fall wird auf ganze Zehner, Hunderter, Tausender... gerundet.

Runden auf End-Ziffern

Im 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 Ziffern

Leider 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-Bereich

Ganze 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.
bitunsignedsigned
vonbisvonbis
80255-128+127
16065535 -32768+32767
3204.29E+09-2.15E+09+2.15E+09
6401.84E+19-9.22E+18+9.22E+18
12803.40E+38-1.70E+38+1.70E+38
Derzeit (!) verwenden die meisten Programme ohne besondere Anweisung für ganze Zahlen den Typ 16-Bit signed (grün unterlegt)

Überschreitung

Wenn 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
B1 = 10^A1
C1 = B1+1
D1 = C1-B1
In Zelle D1 wird zunächst korrekt berechnet
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
intest = x + 1
End Function
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.

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.

Perl

Perl 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;
$c=$b+1; $d=$c-$b;
Dieses Beispiel funktioniert (ergibt $d=1 ) bis $a=15 und versagt bei größeren Zahlen.

Mit dem integer-Pragma (Compiler-Anweisung) wird Integer-Arithmetik erzwungen:
my $d=3.45;
{
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";
}
In diesem Beispiel gilt das integer-Pragma (nur) innerhalb des Blocks {}
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;
my($id,$ic,$ii);
$id = 2**31-3;
for($ii=0;$ii<10;$ii++) {
$ic = $id*2;
print "$id * 2 = $ic\n";
$id++;
}
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.
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;
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";
}
Dieses Beispiel berechnet die Differenz $ei zweier großer ganzer Zahlen. Erhöhen sie den Schleifen-Zähler, z.B. von 100..$bi..110
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.