Hallo listner, und natürlich auch ein Hallo an alle anderen die Bock haben das hier zu lesen,
deine Buchstaben berechnest du dir selber, da du bei der zehner-Berechnung, wie Ceos ja schon schrieb, noch nicht Fehlerfrei bist.
Prüfe daraufhin auch mal deine huni-Berechnung 
Hier mal als Beispiel einen anderen Wert als den von Ceos, damit du siehst wie du auf das 'M' kommst:
Code:
Zahl X=297
einer 7 =X%10 einer + '0' = Zeichen '7'
zehner 29 =(X-einer)/10 zehner + '0' = Hier Zeichen '0' + 29
huni 0 =(X-(zehner*10+einer))/100 huni + '0' = Zeichen '0'
tausend 0 =WENN(X>=1000;1;0) tausend + '0' = Zeichen '0'
Wie du selber nachrechen kannst, kommt bei den Zehnern hier die 29 raus. Die aber auf den ASCII-'0'-Wert von dezimal 48 addiert ergibt dann 77. Das ist dann der Buchstabe 'M'.
Zum Nachrechnen in EXEL kannst du (bei einer deutschen Installation) den Befehl =REST(x;10) nutzen.
Finde ich übrigens sehr gut, dass du nicht 'Fertigfutter' nutzt, sondern erst einmal selber versuchst dem Rätsel auf die Spur zu kommen. \
/ (Die LIB allerdings solltest du aber nicht aus den Augen verlieren da sie von vielen hier benutzt wird. Fehlt sie dir, kannst du die Programme aus dem Forum nicht runterladen und bei dir probieren.)
Um hier aber mal in die Pötte zu kommen, und damit du das auch (ganz bestimmt) komplett verstehst, hier mal ein Hergang, wie man es machen könnte.
In deinem Kommentar hinter "huni=(x-(zehner*10+einer))/100;" schreibst du, dass du den %-Befehl nicht nutzen möchtest, da er zu viel Speicher brauchen würde.
Du hast diesen Befehl aber schon bei der einer-Berechnung eingesetzt. Das bedeutet dann aber, dass der Programmspeicher für diesen Befehl schon benutzt/verbraucht wird. Dann macht es auch Sinn diese Funktion ruhig mehrfach zu benutzten.
Fazit: Entweder nie, oder möglichst häufig eine Funktion benutzen.
Und dann auch noch etwas zur Idee bei den Tausendern anzufangen:
Als erstes eine Frage: Was wäre, wenn in der Zahl Zehntausender drin wären? Upps, das Programm muss dann geändert werden. Nicht schlimm, aber auch nicht schön.
Also doch andersherrum:
Deinen Fehler bei der zehner-Berechnung werden wir gleich ausnutzen!!! Also nicht korrigieren 
Wenn man sich das Ergebnis dieser fehlerhaften Berechnung anschaut, dann ist das ja die ursprüngliche Zahl ohne der letzten Stelle.
Nun ja, auch das ist nur eine Zahl, die wir ja auch noch zerlegen wollen. Auch hier soll die letzte Stelle ermittelt werden um sie danach aber auch wieder abzuschneiden.
Auch dann bleibt wieder nur irgendeine Zahl übrig, die wir schon wieder bearbeiten wollen.
usw. usw. (Hört sich nach einer Aufgabe für eine Schleife an)
Wann ist hier ein Ende in Sicht?
Klar, wenn nach dem Abschneiden der letzten Stelle nichts mehr übrig bleibt.
Kleine Zwischenüberlegung:
Wie viel Platz benötigen wir maximal im Textspeicher? (Deine Variable "unsigned char s[5] = "thze\0";")
Die Zahl x, die 'übersetzt' werden soll wird in deinem Programm in einer "unsigned int"-Variablen gespeichert.
int sind IMMER 16 Bit. Somit 2 ^ 16 - 1 = 65535 als Maximum. Das sind nun 5 Speicherstellen PLUS dem String-Ende-Zeichen '\0' sind somit 6 Speicherstellen, die wir maximal benötigen.
---> unsigned char s[6] = "thze\0";
Aus der Vorbelegung 'thze' machen wir '.....' (sollen 5 Leerzeichen sein), um alle Buchstaben aus der Variablen zu entfernen, da wir gleich in der Schleife nicht mehr alle Stellen bearbeiten werden.
Nun also
Code:
unsigned char s[6] = " \0";
Hier ist aber schon ein kleiner Fehlerteufel in C vergraben.
Wenn wir eine String-Variable mit einem in "" eingeschlossenem Text vorbelegen, dann kopieren wir hier eigentlich den Text zwischen den "" in die Variable. Der Text selber ist aber schon mit den doppelten "" eingeschlossen. Da es Text ist, wird automatisch ein '\n' angehängt.
Somit haben wir nun 5 Leerzeichen, das \n und das automatische \n. Das sind nun 7 Zeichen.
Also machen wir es nun richtig(er) mit:
Code:
unsigned char s[] = " ";
Wir lassen das \n weg und lassen auch die Größenangabe in der []-Klammer weg. Da kümmert sich der C-Compiler besser drum.
Und nun können wir mit der Schleife anfangen. Sie soll so lange rechnen, solange noch eine Zahl vorhanden ist:
Code:
unsigned char s[] = " ";
while (x > 0)
{
}
Oh, oh. Hier ist eine Stolperstelle.
Was ist, wenn wir die Zahl 0 senden wollen? Dann ist x ja von Anfang an schon nicht mehr größer als 0.
Kein Problem: Diesen Sonderfall initialisieren wir gleich in der String-Variablen mit einer 0 am Ende.
Da wir in der Schleife die letzte Stelle unserer Zahl x irgendwie an die richtige Position im Textspeicher schreiben wollen, benötigen wir noch einen Merker an welcher Stelle im Textspeicher wir diese letzte Stelle eintragen müssen. Dieser Merker wird in der Schleife dann bearbeitet, und muss aber vorher natürlich initialisiert werden.
Da wir hinten bei der Zahl x anfangen, muss dieser Merker 'am Ende' vom Textspeicher beginnen. ACHTUNG, nicht ganz am Ende, da das dort stehende '\n' natürlich nicht überschrieben werden darf.
Code:
unsigned char s[] = " 0";
unsigned char Position;
Position = sizeof (s) - 2;
while (x > 0)
{
Position--;
}
Dieses sizeof() verbraucht im übrigen keinen Programmspeicher und keine Rechenzeit, da es schon beim compilieren 'ausrechnet' wie groß deine Variable s ist.
Und warum werden 2 abgezogen?
sizeof() liefert den Speicherbedarf der Variablen. Das sind also unsere 4 Leerstellen und die Initialisierungs-0 PLUS dem '\n' = 6.
Wir wollen das '\n' ja nicht überschreiben: Also -1
Und da der Speicher mit der Zahl in der []-Klammer immer bei 0 beginnt zu zählen und somit bei 0 die erste Speicherstelle meint, müssen wir diese Diskrepanz auch noch berücksichtigen. Und das sind unsere -2
Nun ermitteln wir ohne den %-Befehl die letzte Stelle der Zahl x in einer Hilfsvariablen. Auch hier können wir Speicher sparen, und nehmen auch nur eine char-Variable:
Code:
unsigned char s[] = " 0";
unsigned char Position;
unsigned char LetzteStelle;
Position = sizeof (s) - 2;
while (x > 0)
{
Position--;
LetzteStelle = x - ( (int)(x / 10) * 10 );
}
So weit so gut. Aber halt! Da ist ein Fehler! Position wird ja schon vor der Schleife auf den letzten sinnvollen Platz im Textspeicher gesetzt. Wenn wir also in der Schleife als erstes da noch einen abziehen, ist die Position ja schon verschoben. Also werden wir das gleich korrigieren!
In diesem Schritt machen wir nun noch 3 Dinge. Zum einen nutzen wir nun deinen 'Fehler'. Dann speichern wir die letzte Stelle auch noch im Textspeicher. Und zu guter letzt 'pusten' wir das Ergebnis auf die Schnittstelle.
Code:
unsigned char s[] = " 0";
unsigned char Position;
unsigned char LetzteStelle;
Position = sizeof (s) - 2;
while (x > 0)
{
LetzteStelle = x - ( (int)(x / 10) * 10 );
x = ( x - LetzteStelle ) / 10; // Deinen Rechenfehler nutzen ;-)
s [Position] = LetzteStelle + '0';
Position--;
}
SerWrite (s, sizeof (s) - 1);
Und das war es dann auch schon. 
Und nach der Pflicht natürlich noch die Kür.
Warum funktioniert das Ausrechnen der letzten Stelle ist noch die große Frage?
Da müsste doch eigentlich bei x = 297 dann mit 297 - ( 297 / 10) * 10 ) auch wieder 297 rauskommen.
Der fiese C-Trick hier ist das kleine (int) in der Berechnung.
Das schneidet nämlich alle Nachkommastellen einfach weg. Wir zwingen den Compiler, dass er den Rechenschritt (x / 10) auf alle Fälle nur mit einem Ganzzahlergebnis berechnet. (War auch schon von Ceos angedeutet an anderer Stelle.)
Und dies ist auch das ganze Geheimnis der Funktion.
Wenn du jetzt noch Puste hast, können wir noch die Variable LetzteStelle wegoptimieren.
Was haben wir gerade für einen Trick mit dem (int) gesehen?
Die Rechnung (int)(x / 10) hatte ja schon 'von ganz alleine' die letzte Stelle abgeschnitten. Genau dies benötigen wir aber nun nur noch in deiner 'falschen' Rechnung.
Wenn wir also in der Zeile nur:
x = ( x ) / 10; // Deinen Rechenfehler nutzen
schreiben, dann schneidet auch diese Zeile die letzte Stelle weg, weil unsere Zielvariable x ja auch nur int-Zahlen speichern kann. Wir müssen uns die letzte Stelle hierfür somit nicht merken.
Auch das Speichern der berechneten letzten Stelle können wir schon in der Rechenzeile selber machen. Hier benötigen wir also diese Variable auch nicht mehr.
Und nun wirklich zu guter letzt, kann auch die Initialisierung der Variablen Position verschoben werden an die Stelle, wo wir die Variable 'erfinden'. Und das -- für die Position kann auch noch an anderer Stelle gemacht werden.
Daraus ergibt sich nun:
Code:
unsigned char s[] = " 0";
unsigned char Position = sizeof (s) - 2;
while (x > 0)
{
s [Position--] = x - ((int)(x / 10) * 10) + '0';
x /= 10;
}
SerWrite (s, sizeof (s) - 1);
Ach du meine Güte, ist das eine Minifunktion geworden. Und die ist so auch nicht mehr leicht zu verstehen. Wichtig hier ist also eine gute Kommentierung vom Code. (Lassen wir hier jetzt aber weg.)
Und du weißt nun wie man so etwas demnächst selber machen kann
. Zumindest hoffe ich das du mir folgen konntest.
Wenn nicht, dann frag natürlich und probiere auf alle Fälle weiterhin selber alles aus.
Gruß Sternthaler
Lesezeichen