Ganzzahlen-Funktionen

Abschneiden, Runden und Rest

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 Symmetrische und asymmetrische Varianten
Runden Runden auf Ganze Zahlen oder eine Anzahl Dezimalstellen
Modulo Die Rest-Funktion
Frac Die Nachkomma-Funktion
Große Zahlen Spezielle Probleme großer ganzer Zahlen

Abschneiden

Durch Abschneiden der Nachkomma-Stellen werden Gleitkomma-Zahlen in Ganze Zahlen umgewandelt. Eindeutige Funktions-Namen sind floor, ceil:
Sie liefern immer die nächst-kleinere bzw. -größere ganze Zahl.
Die Funktion int (Ganzzahl) ist leider nicht eindeutig definiert, sie kann sich für x<0 unterschiedlich verhalten.
Achten sie daher bei der Übertragung von Algorithmen auf andere Programmiersprachen besonders auf diese häufig vorkommende Funktion !
In den folgenden Beispielen wird jeweils eine symmetrische isym und eine unsymmetrische Version ifloor vorgestellt, die wie floor arbeitet.
Symmetrisches (symint) und asymmetrisches (floor, ceil) Abschneiden (int ist nicht eindeutig !):
xsymint(x)floor(x)ceil(x)
+3.141592334
0000
-3.141592-3-4-3
Kalkulation
(Excel, OpenOffice):
Die symmetrische Funktion muss selbst programmiert werden, Funktion GANZZAHL arbeitet wie floor.
=GANZZAHL(x)+WENN(x<0;1;0)
=GANZZAHL(x)

Javascript:
Keine int-Funktion. Die symmetrische Funktion muss selbst programmiert werden, floor wird angeboten:
isym = symint(x);
ifloor = Math.floor(x);
function symint(x) {
if(x>0) {return Math.floor(x);}
else {return Math.ceil(x);}
}
Perl:
Funktion int arbeitet symmetrisch, floor muss programmiert werden.
$isym = int($x);
$ifloor = floor($x);
sub floor {
my($x)=@_;
my($f)=int($x);
if($x<0) {$f--;}
return $f;
}

PHP:
Keine int-Funktion. Die symmetrische Funktion muss programmiert werden, floor und ceil werden angeboten.
$isym = symint($x);
$ifloor = floor($x);
function symfloor($x) {
if($x>0) {return floor($x);}
else {return ceil($x);}
}

VBA:
Funktion Fix arbeitet symmetrisch, Funktion Int asymmetrisch wie floor.
isym = Fix(x)
ifloor = Int(x)
Die Standard-Funktionen Int und Fix sind nicht an den Variablen-Typ gebunden. Sie können das Ergebnis nicht nur an Integer, sondern auch an Long oder Double übergeben. Das ist wichtig, wenn sie Zahlenwerte >32767 verarbeiten wollen.
Die Standard-Funktionen zur Typ-Umwandlung arbeiten allerdings anders ! Funktionen CInt und CLng runden das Argument zur nächsten ganzen Zahl.

Runden

Beim Runden auf positive ganze Zahlen wird abgerundet, wenn der Nachkomma-Rest frac(x)<0.5 beträgt, ansonsten wird aufgerundet. Beim Runden auf andere Intervalle verfährt man sinngemäß, d.h. es wird abgerundet, wenn der Rest kleiner als das halbe Intervall ist.
Die Rundung erfolgt immer symmetrisch zu x=0, daher ist round(-0.499)=0 und round(-0.50)=-1
Besondere Fälle sind Rundungen auf andere Intervalle als ganze Zahlen.
Rundung innerhalb von Algorithmen ist nur selten erwünscht, weil dadurch Information (Genauigkeit) verloren geht.
Rundung von Geldbeträgen innerhalb wirtschaftlicher Kalkulationen kann sich unangenehm auswirken, wenn solche Beträge nachfolgend (z.B. mit Stückzahlen) multipliziert werden, und ist daher normalerweise verboten.
Rundung bei der Ausgabe von Gleitkomma-Zahlen an menschliche BenutzerInnen ist meistens erwünscht. Das verbessert Lesbarkeit und Übersicht, und eliminiert Geld-Differenzen <1 Cent. Für die Ausgabe wichtig ist oft die genaue Angabe führender oder folgender Nullen.
Rundung auf beliebige Intervalle
So kann eine Funktion round zum ganzzahligen Runden modifiziert werden, um auf beliebige Intervalle v zu runden:
r = round(x/v) * v
Das Beispiel funktioniert in allen gängigen Programmiersprachen und rundet z.B. Geldbeträge mit v=0.01 auf Cent, mit v=0.2 auf 20 Cent-Stufen, mit v=0.5 auf 50 Cent oder ganze €, mit v=5 auf 5€ oder 10€, mit v=100 auf ganze Hunderter usw.
Einige Programmiersprachen bieten Varianten zur Rundung auf d Dezimalstellen, das entspricht v=10^(-d) bzw. d=-log10(v) dieses Beispiels.

Kalkulation   (Excel, OpenOffice):
Funktion RUNDEN erfordert als 2.Argument die Anzahl Dezimalstellen. Die gezeigten Beispiele runden auf Cent, auf ganze Zahlen und auf Tausender.
=RUNDEN(x;2)
=RUNDEN(x;0)
=RUNDEN(x;-3)

Javascript:
r = Math.round(x);

PHP:
Funktion round rundet auf ganze Zahlen, ab Version 4 kann man optional die Anzahl d dezimaler Nachkomma-Stellen angeben (hier d=2 für Cent-Rundung).
$r = round($x);
$r = round($x,2);
Perl:
Bietet in der Grund-Version keine round-Funktion. Dieses Beispiel rundet die Zahl $x wahlweise auf ganze Zahlen oder auf $d Dezimal-Stellen.
$r = myround($x);
$r = myround($x,$d);

sub myround {
my $c=@_;
my($x,$d)=@_;
if($c<2) {$d=0;}
my $e=10**$d;
$x = $x*$e;
if($x>0) {$x+=0.5;}
else{$x-=0.5}
return (int($x)/$e);
}
Beachten sie die Möglichkeit, Perl (nur am eigenen PC oder Server) durch eine große Anzahl von Modulen, (u.a. für mathematische Funktionen) zu ergänzen.

VBA:
Funktion Round rundet auf ganze Zahlen, man kann als optionales Argument die Anzahl d dezimaler Nachkomma-Stellen angeben (hier d=2 für Cent-Rundung).
r = Round(x)
r = Round(x,2)
Tipp: VBA-Funktion zur Erzeugung gerundeter Zahlen mit Dezimal-Punkt am Ende dieses Absatzes.
Text-(Zeichenkette, String)-Ausgabe
Für die Ausgabe ist ein übersichtliches, manchmal auch genormtes Format erforderlich. Dazu genügt die Rundung nicht immer.
Ganze Zahlen mit führenden Nullen sind für genormte Formate erforderlich, z.B. Datum und Zeit.
Texte aus Zahlen (allgemein):
Um einen String der Länge n=4 zu erhalten, stellt man einen String aus ebenso vielen Nullen "0000" und der Zahl x zusammen, davon werden die n letzten Zeichen isoliert. Für negative Zahlen wird das Verfahren normalerweise nicht angewendet.
Excel und OpenOffice
Führende Nullen lassen im Menü Format | Zellen | Benutzerdefiniert eingestellen: Das Format 0# zeigt 2stellige ganze Zahlen mit 1..2 führenden Nullen an, 00# zeigt 3stellige ganze Zahlen, usw.
Die Funktion =TEXT(C7;"0#") erzeugt Texte 2stelliger Zahlen, =TEXT(C8;"00#") Texte 3stelliger Zahlen usw.
Javascript:
(einfache Version ohne Vorzeichen)
function leading_zero(x,n) {
var s = txt_repeat("0",n);
s+=Math.abs(x).toString();
return s.substr(s.length-n);
}
// Hilfsfunktion zur Text-Wiederholung
function txt_repeat(t,n) {
var s = "";
for(var i=0;i<n;i++) {s+=t;}
return s;
}
Die Funktion ergibt z.B.   leading_zero(123,5)="00123"
Alternativ kann Javascript auch mit RegExp arbeiten.
Perl und PHP:
verwenden die Standardfunktion sprintf zur Erzeugung eines Strings von $n Zeichen aus der ganzen Zahl $x:
$s = sprintf("%0".$n."d",$x);

VBA:
Der String kann z.B. so formuliert werden:
s = Right(String(n, "0") & CStr(x), n)
Tipp: VBA-Funktion zur Erzeugung gerundeter Zahlen mit Dezimal-Punkt am Ende dieses Absatzes.
Gleitkomma-Zahlen mit folgenden Nullen sind für die übersichtliche Darstellung in Tabellen erforderlich.
Das erfordert entweder eine bequeme Standard-Funktion wie sprintf oder die Spaltung einer Zahl in ganze Zahl i=floor(x) und Nachkomma-Stellen f=frac(x). Beide werden in Strings umgewandelt und wieder zusammengesetzt. Dazwischen kann man den Nachkomma-String durch Anfügen von Nullen auf die gewünschte Länge bringen.
Die Funktion ergibt z.B. round_txt(12.3,3)="12.300"

Perl und PHP:
verwenden die Standardfunktion sprintf zur Erzeugung eines Strings mit $n Dezimalstellen aus der Zahl $x:
$s = sprintf("%01.".$n."f",$x);
Javascript:
function round_txt(x,d) {
var v = Math.pow(10,-d);
var xr = Math.round(x/v)*v;
var s = xr.toString();
if(d>0) {
if(s.match(/\./)) {
s.match(/(-?\d*)\.(\d*)/);
var m1 = RegExp.$1;
var m2 = RegExp.$2;
m2+=txt_repeat("0",d);
m2 = m2.substr(0,d);
s = m1+"."+m2;
}
else {
s+="."+txt_repeat("0",d);
}
}
return s;
}
Die Hilfsfunktion txt_repeat finden sie im Absatz oberhalb.
VBA - Erzeugung gerundeter Zahlen-Strings mit Dezimal-Punkt.
Excel eignet sich gut, um Programm-Code für Programmiersprachen zu erzeugen (z.B. aus Tabellen).
Alle gängigen Programmiersprachen verwenden Dezimal-Punkte, deutsche Excel-Versionen jedoch Dezimal-Komma.

Das Beispiel rechts wandelt Zahlen in Strings um. Die Zahl f wird auf n Stellen gerundet und mit Komma-Punkt ausgegeben.
Die Funktion arbeitet auch mit negativen Zahlen.

Ergänzungen für eine professionellen Version:
Argument n optional, Validierung von n (minimale und maximale Grenzwerte), ev. Kontrolle des Variablen-Typs von f
Function float_to_src(f As Double, n As Integer) As String
Dim iv As Integer
Dim r, ri As Double
Dim t As String
r = Round(f, n)
iv = Sgn(r)
r = Abs(r)
ri = Int(r)
r = 1 + r - ri
r = Round(r * 10 ^ n, 0)
t = CStr(ri) & "." & Right(CStr(r), n)
If iv < 0 Then t = "-" & t
float_to_src = t
End Function

Modulo-Funktion  (Divisions-Rest)

Diese Funktion berechnet den Rest r bei Division von x durch n
In der IT-Praxis ist diese Funktion in manchen Fällen als Operator realisiert, in anderen als Funktion.
In den meisten Fällen funktionieren die Standard Modulo-Funktionen mit positiven ganzen Zahlen.
Probleme (Unterschiedliches Verhalten) kann es mit negativen Zahlen und mit Gleitkomma-Zahlen geben.
Testen sie sorgfältig, ob solche Fälle in ihren Programmen vorkommen können, und wie darauf reagiert wird.
Allgemein kann die Modulo-Funktion so ersetzt werden:
mod(x,n) = x - floor(x/n)* n
Der Nachkomma-Anteil frac(x) ist identisch mit mod(x,1)
Asymmetrisches Verhalten wie hier durch amod demonstriert ist selten erwünscht.
xfrac(x)mod(x,3)amod(x,3)
+3.14160.14161.14161.1416
+3000
+2022
+1011
0000
-10-12
-20-21
-3000
-3.1416-0.1416-1.14160.8584
Kalkulation   (Excel, OpenOffice):
Funktion REST arbeitet asymmetrisch und liefert auch von negativen x<0 positive Ergebnisse.
=REST(x;n)
Eine symmetrische Modulo-Funktion erhält man z.B. mit diesen Formeln:
=REST(x;n)-WENN(x<0;n;0)
=rohrest*WENN(rohrest=-n;0;1)
Die erste Zelle berechnet eine 'rohe' symmetrische Modulo-Funktion und wird als rohrest in die 2. Formel eingesetzt. Die Korrektur ist notwenig, um den Fall rohrest=-n abzufangen

Perl
$m = $x % $n;
Nur für ganzzahlige $x,$n verwenden !
Ersatz für Gleitkomma-Variable:
$m = mymod($x,$n);
sub mymod {
my ($x,$n)=@_;
return $x - int($x / $n) * $n;
}

PHP:
$m = $x % $n;
Beide Argumente $x,$n werden ganzzahlig verwendet, d.h. vor Anwendung der Modulo-Funktion abgeschnitten.
Ersatz für Gleitkomma-Variable:
function mymod($x,$n) {
return ($x-floor($x/$n)*$n);
}
Javascript:
m = x % n;
|m| = Math.abs(x % n);

VBA:
m = x Mod n
Nur für Dim x,n,m As Integer verwenden !
Der Operator funktioniert nur bis n<=(2^31)-1
Ersatz für Double-Variable:
m = mymod(x,n)
Function mymod(x As Double, n As Double) As Double
Dim mm As Double
mm = x - Int(x / n) * n
If x < 0 Then mm = mm - n
'mm = Abs(mm)
mymod = mm
End Function
Für positive Reste aktivieren sie die mit ' abgeschaltete Zeile.

Nachkomma-Funktion (frac)

Der Nachkomma-Anteil frac(x) kann theoretisch mit mod(x,1)berechnet werden.
Die Tabelle rechts zeigt, wie sich je nach Bedarf symmetrisches oder asymmetrisches Verhalten erreichen lässt.
xfrac(x)x-floor(x)
+3.14160.14160.1416
000
-3.1416-0.14160.8584
Kalkulation   (Excel, OpenOffice):
Die Nachkomma-Funktion muss selbst programmiert werden. Funktion REST arbeitet asymmetrisch und muss daher für negative Argumente korrigiert werden.
=REST(B4;1)-WENN(B4<0;1;0)

Perl
sub my_frac {
my($x)=@_;
return $x-int($x);
}

PHP:
function my_frac($x) {
if($x>=0) {$f=$x-floor($x);}
else{$f=$x-ceil($x);}
return $f;
}
Javascript:
function my_frac(x) {
if(x>=0) {var f=x-Math.floor(x);}
else {var f=x-Math.ceil(x);}
return f;
}

VBA:
Man kann den Nachkomma-Anteil u.a. dazu verwenden, die Zeit aus einem Datum im Y1900-Format zu isolieren.
Function my_frac(x As Double) As Double
my_frac = x - Fix(x)
End Function

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, d.h. 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.
Kalkulation   (Excel, OpenOffice):
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
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:
in Arbeit
Javascript:
in Arbeit
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.