| 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 !):
|
||||||||||||||||
|
►
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)
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.ifloor = Int(x) 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);
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.$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); ► 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)
♣
Tipp: VBA-Funktion zur Erzeugung gerundeter Zahlen mit Dezimal-Punkt
am Ende dieses Absatzes.
r = Round(x,2) |
|
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ü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) {
Die Funktion ergibt z.B. leading_zero(123,5)="00123"
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; ♦ 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) {
Die Hilfsfunktion txt_repeat finden sie im Absatz oberhalb.
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; |
|
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)
End Function
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 |
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.
|
||||||||||||||||||||||||||||||||||||||||
|
►
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)
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=rohrest*WENN(rohrest=-n;0;1) ► 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)
Für positive Reste aktivieren sie die mit '
abgeschaltete Zeile.
Function mymod(x As Double, n As Double) As Double Dim mm As Double
mm = x - Int(x / n) * n
mymod = mmIf x < 0 Then mm = mm - n 'mm = Abs(mm)
End Function |
||||||||||||||||||||||||||||||||||||||||
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. |
|
||||||||||||
|
►
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.
|
|
||||||||||||||||||||||||||||||||||
|
Ü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
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 |
►
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: 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;
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. |
||||||||||||||||||||||||||||||||||