Die Computerseite von Eckart Winkler
Lange Integer-Zahlen in C - Teil 2

 

Dieser Artikel ist im Heise-Verlag in der Zeitschrift "c't", Ausgabe 04/1989 erschienen. Er ist hier aus Gründen der besseren Übersicht dreigeteilt. Der Text ist Grundlage des Artikels "Primzahltests und Primfaktorzerlegung". Links:



Die Realisierung

Da über die verwendete Datenstruktur nun Klarheit besteht, kommen wir zu den Unterprogrammen, die für den Umgang mit longint-Zahlen nötig sind. Sie sind zusammen mit den nötigen Definitionen im folgenden Programm-Listing enthalten. Dieses soll eine Art Bibliothek darstellen. Der Inhalt sollte unter dem Namen "longcalc.c" abgespeichert werden.

Wollen wir mit longint-Zahlen rechnen, müssen wir dem Preprozessor dies durch eine include-Anweisung mitteilen. Die Anzahl der Elemente eines derartigen Vektors ist durch die Konstante MaxNr gegeben, diese wird bei Benutzung durch eine define-Anweisung festgelegt. Auf diese Weise ist es möglich, ohne Änderung der Datei "longcalc.c" beliebig lange Zahlen zu benutzen.

/* longcalc.c */

#define intdig 10
#define HBit 0x40000000
#define RBits 0x7FFFFFFF
#define carry(x) (x<0)
#define pos(x) (x&=RBits)

typedef long longint[MaxNr];
typedef char string[intdig*MaxNr];

longint einsl={1,1},zweil={1,2},dreil={1,3},vierl={1,4};
longint zehnl={1,10},hundertl={1,100};

/*   Umwandlung string -> longint   */
stol(string s, longint z)
{
  int i;
  long w1;
  longint t,w;
  i=0; longl(1L,t); longl(0L,z);
  while (s[i]!='\0') i++;
  while (i) {
    i--; w1=s[i]-'0'; longl(w1,w);
    multl(w,t,w); addl(w,z,z); multl(t,zehnl,t); }
}

/*   Umwandlung longint -> string   */
ltos(longint z, string s)
{
  int i,j;
  char ch;
  longint w,y;
  if (eq0l(z)) { s[0]='0'; s[1]='\0'; return; }
  transl(z,y); i=0;
  while (gt0l(y)) {
    divmodl(y,zehnl,y,w); s[i]=w[1]+'0'; i++; }
  s[i]='\0'; i--; j=0;
  while (i>j) {
    ch=s[i]; s[i]=s[j]; s[j]=ch; i--; j++; }
}

printl(longint x)           /*   Ausgabe einer longint-Zahl   */
{
  string s;
  ltos(x,s); printf("%s",s);
}

scanl(longint x)            /*   Eingabe einer longint-Zahl   */
{
  string s;
  scanf("%s",s); stol(s,x);
}

longl(long x, longint y)          /*   Umwandlung long-longint   */
{
  y[0]=1;
  if (x>=0) y[1]=x; else y[1]=x&RBits;
}

transl(longint x, longint y)         /*   y=x   */
{
  int i;
  for (i=0;i<=x[0];i++) y[i]=x[i];
}

eql(longint x, longint y)            /*   x==y   */
{
  int i;
  for (i=0;i<=x[0];i++)
    if (x[i]!=y[i]) return 0;
  return 1;
}

nel(longint x, longint y)            /*   x!=y   */
{
  int i;
  for (i=0;i<=x[0];i++)
    if (x[i]!=y[i]) return 1;
  return 0;
}

gtl(longint x, longint y)            /*   x>y   */
{
  int i;
  if (x[0]>y[0]) return 1;
  if (x[0]<y[0]) return 0;
  for (i=x[0];i>0;i--) {
    if (x[i]>y[i]) return 1;
    if (x[i]<y[i]) return 0; }
  return 0;
}

gel(longint x, longint y)            /*   x>=y   */
{
  int i;
  if (x[0]>y[0]) return 1;
  if (x[0]<y[0]) return 0;
  for (i=x[0];i>0;i--) {
    if (x[i]>y[i]) return 1;
    if (x[i]<y[i]) return 0; }
  return 1;
}

ltl(longint x, longint y)            /*   x<y   */
{
  int i;
  if (x[0]>y[0]) return 0;
  if (x[0]<y[0]) return 1;
  for (i=x[0];i>0;i--) {
    if (x[i]>y[i]) return 0;
    if (x[i]<y[i]) return 1; }
  return 0;
}

lel(longint x, longint y)            /*   x<=y   */
{
  int i;
  if (x[0]>y[0]) return 0;
  if (x[0]<y[0]) return 1;
  for (i=x[0];i>0;i--) {
    if (x[i]>y[i]) return 0;
    if (x[i]<y[i]) return 1; }
  return 1;
}

eq0l(longint x)             /*   x==0   */
{
  return !x[x[0]];
}

gt0l(longint x)             /*   x>0   */
{
  return x[x[0]]>0;
}

oddl(longint x)             /*   x ungerade ?   */
{
  return x[1]&1;
}

addl(longint x, longint y, longint z)         /*   z=x+y   */
{
  int i,n;
  if (x[0]<y[0]) {
    for (i=x[0]+1;i<=y[0];i++) x[i]=0; n=y[0]; }
  else {
    for (i=y[0]+1;i<=x[0];i++) y[i]=0; n=x[0]; }
  z[1]=x[1]+y[1]; i=1;
  while (i<n) {
    if (carry(z[i])) {
      pos(z[i]); i++; z[i]=x[i]+y[i]+1; }
    else {
      i++; z[i]=x[i]+y[i]; } }
  if (carry(z[n])) { pos(z[n]); n++; z[n]=1; }
  z[0]=n;
}

subl(longint x, longint y, longint z)         /*   z=x-y   */
{
  int i;
  for (i=y[0]+1;i<=x[0];i++) y[i]=0;
  z[1]=x[1]-y[1]; i=1;
  while (i<x[0]) {
    if (carry(z[i])) {
      pos(z[i]); i++; z[i]=x[i]-y[i]-1; }
    else {
      i++; z[i]=x[i]-y[i]; } }
  while (!z[i] && i>1) i--;
  z[0]=i;
}

incl(longint x)             /*   x=x+1   */
{
  int i;
  x[1]++; i=1;
  while (carry(x[i]) && i<x[0]) { pos(x[i]); i++; x[i]++; }
  if (i==x[0] && carry(x[i])) { pos(x[i]); x[0]++; x[x[0]]=1; }
}

decl(longint x)             /*   x=x-1   */
{
  int i;
  x[1]--; i=1;
  while (carry(x[i]) && i<x[0]) { pos(x[i]); i++; x[i]--; }
  if (!x[x[0]] && x[0]>1) x[0]--;
}

div2l(longint x)            /*   x=x/2   */
{
  int i;
  for (i=1;i<x[0];i++)
    if (x[i+1]&1) x[i]=(x[i]>>1)|HBit; else x[i]>>=1;
  x[x[0]]>>=1;
  if (!x[x[0]]) x[0]--;
}

mult2l(longint x)           /*   x=x*2   */
{
  int i;
  x[1]<<=1; i=1;
  while (i<x[0]) {
    if (carry(x[i])) {
      pos(x[i]); i++; x[i]=(x[i]<<1)|1; }
    else {
      i++; x[i]<<=1; } }
  if (carry(x[i])) { pos(x[i]); i++; x[i]=1; x[0]++; }
}

multl(longint x, longint y, longint z)        /*   z=x*y   */
{
  longint a,b,r;
  if (gtl(x,y)) { transl(x,a); transl(y,b); }
    else { transl(x,b); transl(y,a); }
  longl(0L,r);
  while (gt0l(b)) {
    if (oddl(b)) addl(r,a,r);
    mult2l(a); div2l(b); }
  transl(r,z);
}

divl(longint x, longint y, longint z)         /*   z=x div y   */
{
  longint a,b,y1,r;
  longl(0L,r); transl(x,a); transl(y,b); transl(b,y1);
  while (lel(b,a)) mult2l(b);
  while (nel(b,y1))
    {
    mult2l(r); div2l(b);
    if (lel(b,a)) { subl(a,b,a); incl(r); }
    }
  transl(r,z);
}

modl(longint x, longint y, longint z)         /*   z=x mod y   */
{
  longint a,b,y1,r;
  longl(0L,r); transl(x,a); transl(y,b); transl(b,y1);
  while (lel(b,a)) mult2l(b);
  while (nel(b,y1))
    {
    mult2l(r); div2l(b);
    if (lel(b,a)) { subl(a,b,a); incl(r); }
    }
  transl(a,z);
}

/*   d=x div y ; m=x mod y   */
divmodl(longint x, longint y, longint d, longint m)
{
  longint a,b,y1,r;
  longl(0L,r); transl(x,a); transl(y,b); transl(b,y1);
  while (lel(b,a)) mult2l(b);
  while (nel(b,y1))
    {
    mult2l(r); div2l(b);
    if (lel(b,a)) { subl(a,b,a); incl(r); }
    }
  transl(r,d); transl(a,m);
}

powl(longint a, longint s, longint z)         /*   z=a^s   */
{
  longint x,y,h,r;
  longl(1L,r); transl(a,x); transl(s,y);
  while (gt0l(y))
    {
    if (oddl(y)) multl(r,x,r);
    multl(x,x,x); div2l(y);
    }
  transl(r,z);
}

/*   z = a*b mod n   */
abmodn(longint a, longint b, longint n, longint z)
{
  longint x,y,s;
  if (gtl(a,b)) { transl(a,x); transl(b,y); }
    else { transl(b,x); transl(a,y); }
  longl(0L,s);
  while (gt0l(y)) {
    if (oddl(y)) {
      addl(s,x,s);
      if (gel(s,n)) subl(s,n,s); }
    mult2l(x);
    if (gel(x,n)) subl(x,n,x);
    div2l(y); }
  transl(s,z);
}

/*   z = a^s mod n   */
ahsmodn(longint a, longint s, longint n, longint z)
{
  longint x,y,r;
  transl(a,x); transl(s,y); longl(1L,r);
  while (gt0l(y)) {
    if (oddl(y)) abmodn(r,x,n,r);
    abmodn(x,x,n,x); div2l(y); }
  transl(r,z);
}

/*   groesster gemeinsamer Teiler   */
ggtl(longint n, longint m, longint f)
{
  longint p,q,r;
  transl(n,p); transl(m,q);
  do {
    modl(p,q,r); transl(q,p); transl(r,q); }
  while (gt0l(r));
  transl(p,f);
}

fakl(longint x, longint f)           /*   r=x!   */
{
  longint r,y;
  longl(1L,y); longl(1L,r);
  while(lel(y,x)) {
    multl(r,y,r); incl(y); }
  transl(r,f);
}


Und so funktioniert es

Die erste Prozedur, die für uns momentan von Interesse ist, ist longl. Wie bei den meisten Unterprogrammen und auch schon bei unseren "Konstanten" deutet das "l" am Ende darauf hin, daß hier longint-Zahlen im Spiel sind. Der Zweck von longl ist die Umwandlung einer long-Zahl in eine longint-Zahl, was recht einfach erreicht werden kann. Soll hiermit der Wert einer Integer- Variablen x in longint verwandelt werden, muß man zusätzlich eine "cast"-Umwandlung benutzen. Der Aufruf sieht dann so aus: longl((long)x,s).

Wichtig ist: Hier wie bei allen anderen Routinen werden die Eingabewerte einer Funktion an den Anfang der Argumentliste geschrieben, Ergebnisse stehen am Ende. Dies ist konsequent eingehalten, auch wenn es der üblichen Operatorenschreibweise widerspricht. So schreibt man bei der nächsten Prozedur, die lediglich eine longint-Zahl kopiert, transl(x,s), auch wenn dies bei Vorhandensein eines entsprechenden Operators s=x heißen müßte.


Vergleichsoperationen

Zu den nun folgenden Vergleichsoperationen ist kaum etwas zu sagen. Die Namensgebung orientiert sich an den Vergleichen in Fortran. Die Routinen geben jeweils 0 oder 1 zurück, können also direkt in Vergleichsausdrücken verwendet werden. Zwei Spezialroutinen wurden zum Vergleich mit 0 implementiert. Diese können helfen, Zeit zu sparen. Da es keine negativen longint- Zahlen gibt, ist gt0l identisch zu einem Test "ungleich 0". Außerdem finden wir hier den Test "ungerade". Dieser ist durch eine einzige Bit-Operation realisiert.


Addition und Subtraktion

Interessanter ist beispielsweise addl, womit zwei longint-Zahlen x und y addiert werden. Das Prinzip ist einfach. Es werden immer zwei Elemente x[i] und y[i] zusammengezählt und z[i] zugewiesen. Ist das Resultat negativ, hat ein Überlauf stattgefunden. Wir löschen dann das betreffende Bit und addieren 1 zum nächst höheren Element von z. Für beide Aktionen, Erkennen des Übertrags und Löschen des Übertrags-Bits, haben wir ganz am Anfang zwei nützliche Makros definiert. Die Funktionsweise von carry ist wohl klar. Zum Löschen des höchstwertigen Bits durch pos wird das Element von z mit der Konstanten RBits durch & (and) verknüpft. In RBits sind alle Bits gesetzt, außer dem höchstwertigen natürlich.

So weit, so gut. Leider gibt es Ausnahmefälle, die berücksichtigt werden müssen. Diese treten zutage, wenn x und y unterschiedlich lang sind, also unterschiedliche Konstanten im nullten Vektor- Element stehen haben. Ist so die longint-Zahl x etwa 3 Elemente lang, y aber 5, dürfen die Elemente 4 und 5 von x nicht verwendet werden, da dort unbekannte Werte stehen. Hier müssen also die Werte von y nach z übertragen werden, der Übertrag mitgerechnet.

Der andere Fall wäre genau umgekehrt, x ist länger als y. Am Ende werden dann die Elemente von x nach z übertragen. Schließlich gibt es auch den Fall, daß beide gleich lang sind. Zunächst muß aber auch erkannt werden, welcher Fall vorliegt. Summa summarum also höchst unangenehm für den Programmierer. Wir behelfen uns hier mit dem einfachen Trick, daß wir die fehlenden Elemente der kürzeren Zahl, also x oder y, mit 0 vollschreiben. Dann können wir nämlich beide als gleichlang betrachten und brauchen keine weitere Fallunterscheidung vorzunehmen.

Dies ist zwar eine elegante Lösung, kostet aber etwas Zeit, jedoch erreichen wir auf diese Weise den am leichtesten zu lesenden Quellcode. Zudem ist auch nicht zu erwarten, daß die Längen der zu addierenden Zahlen ständig stark differieren. Und schließlich gibt es ja auch noch die Spezialroutinen incl und decl. Die eigentliche Addition führen wir wie besprochen in der while-Schleife aus. Die einzige Abfrage betrifft hier den Test auf Übertrag. Am Anfang wurde n als zunächst provisorische Länge von z festgesetzt. Dies kann nun eventuell noch um 1 erhöht werden. Diese Überprüfung steht am Ende der Prozedur.

Ähnlich wie bei addl gehen wir bei subl vor. Hier muß y die kleinere der beiden Zahlen x und y sein, somit wird y mit 0 aufgefüllt. Erkennen des Übertrags und Löschen des Bits werden wieder durch die bereits besprochenen Makros carry und pos ausgeführt. In den meisten Fällen wird die Länge von z gleich der von x sein. Sind x und y jedoch annähernd gleich groß, kann sie noch erniedrigt werden. Dies geschieht in der letzten Schleife.

Interessant ist die Frage: Welches Resultat erhalten wir bei Aufrufen wir addl(x,x,x) oder subl(x,a,x)? Mit anderen Worten, können für Ausgabewerte dieser Funktionen dieselben Variablen verwendet werden wie für die Eingabewerte? Immerhin werden bei addl und subl die Ausgabevariablen ja schon teilweise mit Werten belegt, wenn die Eingabevariablen noch nicht vollständig gelesen sind. Somit könnte es zu einem Fehlverhalten kommen.

Wenn wir die beiden Routinen aber sorgfältig durchsehen, erkennen wir, daß das hier nicht der Fall ist. Denn das Element z[i] wird immer erst gesetzt, wenn x[i] und y[i] nicht mehr benötigt werden. An dieser Stelle ist anzumerken, daß das bei den meisten der anderen Routinen nicht automatisch der Fall ist. Damit aber alle Prozeduren beliebig benutzt werden können, wurden sie so programmiert, daß das Ergebnis erst in einer anderen Variablen zusammengestellt wird. Erst im letzten Schritt wird es von dieser Hilfsvariablen in die eigentliche Ergebnisvariable kopiert.

Was passiert eigentlich, wenn wir mit subl eine Zahl von einer kleineren Zahl abziehen wollen? Jedenfalls nichts Schlimmes. Der Rechner stürzt nicht ab, und ein Resultat gibt es auch. Nur können wir mit dem nicht viel anfangen. Ungünstiger endet es, wenn wir z.B. bei einer Addition den durch MaxNr definierten Rechenbereich überschreiten. Da spielt der Rechner meist nicht mehr mit.

Nun, worauf hier angespielt werden sollte, ist: Wir haben in die Prozeduren keinerlei Maßnahmen gegen Fehlbedienung eingebaut. Hierbei folgen wir im Grunde voll und ganz der C-Philosophie: Der Anwender ist sich selbst verantwortlich, er hat sich selbst um die korrekte Anwendung der Routinen zu kümmern. Dies ist allerdings nicht der Grund. Vielmehr sparen wir dadurch Zeit. Wollten wir beispielsweise bei der Addition einen Überlauf vermeiden, müßten wir in jedem Schritt abfragen, ob die obere Grenze schon erreicht ist. Und das dauert... Daß wir derartige Fehlerabfragen nun nicht programmieren müssen, ist ein angenehmer Nebeneffekt.

Weiter im Text: incl(x) erhöht x um 1, decl(x) erniedrigt x um 1. Hier werden dieselben Mechanismen angewandt wie bei addl und subl, natürlich vereinfacht sich einiges. Die einzige Addition bzw. Subtraktion wird im Element x[1] durchgeführt, alles andere betrifft nur einen eventuellen Übertrag. Ohne große Änderung könnte man Routinen programmieren, die x statt um 1 um eine beliebige long-Zahl p vergrößern. Diese müßte natürlich in der Parameterliste übergeben werden. Statt x[1]++ müßte man dann x[1]+=p schreiben, analog x[1]-=p statt x[1]--.


Multiplikation und Division

Für Multiplikation und Division verwenden wir die in [1] auf den Seiten 42 und 43 angegebenen Algorithmen. Sie sind dort in Pascal- ähnlicher Notation zu finden. Grundlegende Routinen sind jeweils Verdoppelung und Halbierung.

Eine Division durch 2 entspricht genau einem Rechtsshift um ein Bit. Dies wird aus der Binärdarstellung klar. Hier zeigt sich der Vorteil, daß wir C als Programmiersprache gewählt haben. Denn hier existieren eigens für solche Zwecke Shift-Befehle. Wir müssen also lediglich den gesamten Vektor um ein Bit nach rechts shiften. Überträge sind zu berücksichtigen, wenn bei einem Vektor-Element das nullte Bit gesetzt ist. Dieses muß dann zum nächsten Element übertragen werden, und zwar an die Stelle 30. Hierfür haben wir am Anfang die Konstante HBit definiert, die mit dem jeweiligen Element durch | (or) verknüpft werden muß.

Bei der Multiplikation mit 2, die in mult2l realisiert ist, ist hingegen ein Linksshift erforderlich. Der Übertrag kann hier wieder durch carry(x) erkannt werden. Am Ende muß noch getestet werden, ob ein Gesamtübertrag aufgetreten ist. Dann nämlich wird die Länge der Zahl um 1 erhöht.

Nun kommen die eigentlichen Unterprogramme zur Multiplikation und Division. Die Algorithmen aus [1] sind direkt in C-Syntax übersetzt, selbstverständlich mit den Routinen für longint-Zahlen. Der Divisions-Algorithmus ist gleich dreimal implementiert, er liefert nämlich neben dem Resultat der Division auch den Rest. So ist die Prozedur modl zum Erhalt des Rests vorgesehen, divmodl liefert sowohl Ergebnis als auch Rest der Division.

powl berechnet a^s. Hierzu sind nicht s-1 Multiplikationen nötig, sondern etwas mehr als der Zweierlogarithmus von s. Dies natürlich nur, wenn wir die Potenzierung so wie hier programmieren. Ähnlich funktionieren abmodn und ahsmodn, die Produkt und Potenz modulo n ermitteln. Die Berechnung des größten gemeinsamen Teilers, ggtl, benutzt den Euklidischen Algorithmus. Dieser ist hinlänglich bekannt. Bleibt nur noch die Berechnung der Fakultät von x. Hier werden einfach die erforderlichen Zahlen 1 bis x miteinander multipliziert.


Ein- und Ausgabe

Wir holen nun das nach, was wir zu Beginn übersprungen haben, nämlich die Routinen für Ein- und Ausgabe. Da haben wir zunächst zwei Prozeduren zur Umwandlung eines Strings in eine longint-Zahl und umgekehrt. Der String muß die Dezimaldarstellung einer Zahl beinhalten, darf also nur aus Ziffern bestehen. Der Typ string ist eigens zu diesem Zweck definiert worden. Daß dies ein Vektor vom Typ char ist, ist klar. Fraglich ist lediglich die Länge, die hier anzusetzen ist.

Dazu müssen wir wissen, wieviele Dezimalstellen ein Element einer longint-Zahl liefert. Hierzu berechnet man den Zehnerlogarithmus der größten darstellbaren long-zahl, also von 2^31-1=2147483647. Der Logarithmus davon ist 9.33, wir runden dies auf 10, was als Konstante intdig vereinbart wird. Ein String besteht demnach aus intdig*MaxNr Zeichen. Diese Zahl ist normalerweise etwas zu groß, mit dieser Tatsache muß der Anwender aber fertig werden.

Nun zu den Umwandlungsprozeduren. In stol (Umwandlung string nach longint) holen wir uns in jedem Schritt ein Element des Strings. Von diesem s[i] ziehen wir den Wert des Zeichens '0' ab, um den Zahlwert des Zeichens zu erhalten. Dieser wird nach Umwandlung in longint mit der Zehnerpotenz, entsprechend dem Stellenwert, multipliziert. Das Resultat wird zu der Zahl z hinzuaddiert, die schließlich das Endergebnis enthält. Die jeweils benötigte Zehnerpotenz ist in t gespeichert. Sie muß nicht in jedem Schritt von Grund auf neu berechnet werden, sondern aus dem alten Wert durch Multiplikation mit 10. Das Ende eines Strings wird durch ein Nullbyte gekennzeichnet, geschrieben '\0'. Die Abfrage hiernach dient uns als Abbruchbedingung.

Der umgekehrte Weg wird in ltos beschritten. Eine longint-Zahl ist gegeben. Gesucht ist der String, der die Dezimaldarstellung dieser Zahl enthält. Hier wenden wir in jedem Schritt die Prozedur divmodl an, die uns Ergebnis und Rest der Division durch 10 liefert. Die niederwertigste Ziffer entspricht diesem Rest. Wir erhalten sie als longint-Zahl. Wir wissen aber sicher, daß sich ihr Wert zwischen 0 und 9 bewegt. Deshalb können wir sie einfach als w[1] ansprechen, womit wir auf die übliche Weise weiterrechnen können. Addition von '0' ergibt den Zeichencode, der direkt an die richtige Stelle des Strings s geschrieben wird.

Das Ergebnis unserer Division durch 10 muß auf dieselbe Weise weiterbehandelt werden, deshalb haben wir hierfür auch dieselbe Variable y eingesetzt. Dieser Vorgang geht so lange, wie y noch ungleich 0 ist. Ans Ende wird schließlich das Nullbyte '\0' geschrieben. Wir müssen uns nun klar darüber werden, daß wir unsere Dezimaldarstellung genau umgekehrt in den String s gespeichert haben. Anders geht es aber auch nicht, da wir von vorneherein nicht wissen, wieviele Dezimalziffern die Zahl hat. Das ist aber nicht weiter tragisch: In der letzten Schleife drehen wir die Richtung einfach um und haben so das korrekte Resultat.

Ist z=0, müssen wir dies als Sonderfall handhaben, da die while- Schleife überhaupt nicht durchlaufen würde. Deshalb fragen wir dies bereits zu Beginn ab. Falls der Test erfüllt ist, liefern wir den String "0" zurück und sind aller Sorgen ledig.

Wenn auch die Prozedur ltos wohl nur selten direkt Verwendung finden wird, haben wir mit stol ein einfaches Mittel in der Hand, longint-Zahlen mit Werten zu belegen, die über die Größe einer long-Zahl hinausgehen. So weist stol("12345678912345",s) der longint-Variablen s den Wert 12345678912345 zu. Für kleine Zahlen sollte man dennoch longl benutzen, da dies schneller geht.

Für Ein- und Ausgabe sind die Routinen printl und scanl gedacht. Sie sind in der Namensgebung an die Standardroutinen printf und scanf angepaßt, funktionieren aber ohne Formatstring. Die Realisierung ist sehr einfach, da die Hauptprogrammierarbeit in ltos und stol steckt. Bei diesen Routinen zeigt sich der einzige Nachteil der binären gegenüber der dezimalen Darstellung unserer longint-Zahlen. Sie sind nämlich recht langsam. Im Vergleich zu allen anderen Operationen kommen sie jedoch so selten vor, daß der Nachteil nicht sehr groß ist.


 

Übersicht Programmiersprache C Index