Hi, ich trete gerade auf der Stelle. Wie formatiere ich in C eine Zahl oder einen String? Meine Variable (int_8) kann den Inhalt 0 bis 100 annehmen. Aus 0 bis 100 soll 0.0 bis 10.0 werden (am besten noch rechtsbündig, aber das ist das kleinere Problem). Also z. B.: 0 -> 0.0 5 -> 0.5 10 -> 1.0 15 -> 1.5 50 -> 5.0 100 -> 10.0 Das Ergebnis soll dann in einem String stehen, den ich als String weiterverarbeite. Ich will es NICHT auf dem Bildschirm ausgeben. itoa kann nur Integer ftoa und gcvt kennt offenbar mein Compiler nicht. Ich arbeite mit AVR-Studio 4.19. sprintf(result,"%f",num) funktioniert bei mir auch nur mit Integer Mein Wissensstand entspricht leider meinem Nicknamen.
Newbie schrieb: > sprintf(result,"%f",num) eher: sprintf_s(result, sizeof(resault), "%.1f", num); Hier unter "precision" - https://www.cplusplus.com/reference/cstdio/printf/
Irgend W. schrieb: >> sprintf(result,"%f",num) > > eher: sprintf_s(result, sizeof(resault), "%.1f", num); oder noch eher: sprintf_s(result, sizeof(resault), "%.1f", num/10.);
Newbie schrieb: > sprintf(result,"%f",num) funktioniert bei mir auch nur mit Integer Doch, mit den richtigen Compiler/Linker Einstellungen geht das. Die finden sich in der AVR Libc doku.
Newbie schrieb: > Meine Variable (int_8) kann den Inhalt 0 bis 100 annehmen. > > Aus 0 bis 100 soll 0.0 bis 10.0 werden (am besten noch rechtsbündig, > aber das ist das kleinere Problem). Das ist aber kein Fließkomma, sondern Festkommaarithmetik.
Zu Fuß ginge es z. B. so (Pseudocode; d als Hilfsvariable deklarieren):
1 | IF (i==100) { |
2 | s = '10.0' |
3 | } |
4 | ELSE { |
5 | d = i/10; |
6 | s = ' ' + Char(d + 48) + '.' + Char(i - 10*d + 48) |
7 | } |
Die darinsteckende Mathematik kannst Du Dir ja selbst überlegen. Die Addition "+ 48" kommt daher, weil das Zeichen "0" den ASCII-Code 48 hat.
Newbie schrieb: > Hi, ich trete gerade auf der Stelle. Wie formatiere ich in C eine Zahl > oder einen String? > > Meine Variable (int_8) kann den Inhalt 0 bis 100 annehmen. Newbie schrieb: > Mein Wissensstand entspricht leider meinem Nicknamen. Tja, das sehe ich. Aber keine Angst, man lernt mit der Zeit dazu - aber nur dann, wenn man gewillt ist, die Dinge verstehen zu lernen. Zunächst merke, daß deine Variable signed char Werte von -128 bis +127 annehmen kann. Wenn du da nur die Werte 0 bis 100 zu benutzen gedenkst, ist das deine Sache. Als nächstes beißt sich deine Vorstellung über den Dezimalpunkt bzw. gebrochenen Teil deiner Zahl mit der landläufigen Vorstellung, daß man in einem Byte nur Integerzahlen unterbringen kann. Es geht im Prinzip alles, aber bei einer Zahlenbasis von 10 wird es nicht wirklich schön. Das kommt daher, daß es leichter ist, den Bits in dem Byte eine ganzzahlige Wertigkeit zuzuordnen, wenn die Zahlenbasis eine Potenz von 2 ist. Das einfachste und überschaubarste ist bei deinem Vorhaben also, die Ziffernfolge durch Subtraktion zu ermitteln: 1. Schritt: Festlegen des Vorzeichens und Betrag der Zahl bilden 2. Schritt: Festlegen, ob die erste Ziffer eine 1 oder eine 0 ist und ggf. 100 abziehen 3. Schritt: Ermitteln der zweiten Ziffer durch wiederholtes Abziehen von 10, bis die Zahl gerade noch nicht negativ wird 4. Schritt: die dritte Ziffer ergibt sich durch den übrig bleibenden Rest, der 0..9 sein kann. 5. "Versäubern" der drei Ziffern und des Vorzeichens nach deinem Gusto. Normalerweise wil man ein '+' nicht immer vor jeder positiven Zahl sehen. Ebenso will man zumeist nicht '03' sehen, sondern nur '3'. Bei deiner Ausgabe willst du aber einen Dezimalpunkt vor der letzten Stelle sehen. Also muß da die '0' direkt vor dem Dezimalpunkt stehen bleiben. Das sind alles recht einfache Überlegungen, die dir aber den Ablauf einer Ausgabekonvertierung erklären können. Nun ja, wenn du es wenigstens einigermaßen klug programmierst, dann wird das Ganze wahrscheinlich viel weniger Maschinencode erzeugen, als wenn du sowas wie sprintf einbindest. Und du hast damit wieder etwas mehr gelernt. W.S.
Irgend W. schrieb: > eher: sprintf_s(result, sizeof(resault), "%.1f", num); Ist blöd, wenn result ein Zeiger ist.
Leider bin ich noch nicht durchgestiegen. Ansätze hätte ich sogar verschiedene. Meine Idee wäre jetzt gewesen den String zu zerlegen und einen Punkt einzufügen. In anderen Programmiersprachen kinderleicht (eine Zeile). Aber hier hänge ich bei jeder zweiten Zeile, weil man in C für jeden Furz selber eine Funktion schreiben muss. Entweder stimmte der Typ nicht, die Variable war nicht oder falsch deklariert oder eine andere Fehlermeldung folterte mich :( Nur den String an einer bestimmten Position in zwei Teile zerlegen, ein Riesending. Hab es nicht richtig hinbekommen. Am Ende kamen seltsame Ergebnisse raus... Beim Beispiel https://www.mikrocontroller.net/articles/Festkommaarithmetik schaffe ich es leider nicht das Ergebnis an mein Display zu übergeben, weil ich einfach nicht weiß wie. Im Listing sind übrigens auch Fehler (frac statt flag). Ich weiß, dass man nicht einfach irgendwas zusammenklatschen sollte, ohne es zu verstehen. Aber ich brauche das eigentlich nur, um den prellenden Pollin-Encoder nutzen zu können, der mit dem Beispielcode aus dem Forum super läuft. Der rastet halt mechanisch gut ein. Die anderen Encoder gehen alle so leicht, man verstellt sie leicht beim Drücken. Mein Code oder besser mein Versuch ist noch viel zu bruchstückhaft um ihn hier zu zeigen. Aber danke für die Denkanstöße, die mir später sicher noch nützlich sind.
> Aber hier hänge ich bei jeder zweiten Zeile, weil man in C für jeden > Furz selber eine Funktion schreiben muss. Deshalb ist C so klein, schnell und hardwarenah. :) Und das was du da willst sind eigentlich die banalsten Grundlagen. Und da hilft auch nicht lesen im Internet sondern ein Buch das einem alles mal strukturiert vorbetet/erklaert und das man durcharbeitet. Olaf
Meine Idee wäre jetzt gewesen den String zu zerlegen und einen Punkt einzufügen.
1 | #include <stdio.h> |
2 | #include <stdint.h> |
3 | #include <assert.h> |
4 | |
5 | char* toString(char* target, size_t size, int8_t value) |
6 | {
|
7 | assert(value <= 100 && value >= 0); |
8 | assert(size >= 5); |
9 | |
10 | snprintf(target, size, "%03d", value); |
11 | target[3] = target[2]; |
12 | target[2] = '.'; |
13 | if (target[0] == '0') |
14 | target[0] = ' '; |
15 | |
16 | return target; |
17 | }
|
18 | |
19 | int main() |
20 | {
|
21 | char buf[5]; |
22 | |
23 | printf("%s\n", toString(buf, sizeof buf, 1)); |
24 | printf("%s\n", toString(buf, sizeof buf, 10)); |
25 | printf("%s\n", toString(buf, sizeof buf, 100)); |
26 | }
|
Newbie schrieb: > In anderen Programmiersprachen kinderleicht (eine Zeile). > Aber hier hänge ich bei jeder zweiten Zeile, weil man in C für jeden > Furz selber eine Funktion schreiben muss. Entweder stimmte der Typ > nicht, die Variable war nicht oder falsch deklariert oder eine andere > Fehlermeldung folterte mich :( Nun ist es aber auch in vielen anderen Programmiersprachen so, dass Variablen deklariert und vom richtigen Typ sein müssen, damit es funktioniert.
:
Bearbeitet durch User
EAF schrieb: > Doch, mit den richtigen Compiler/Linker Einstellungen geht das. > Die finden sich in der AVR Libc doku. Ich zitiere einfach nochmal EAFs Antwort... die einzig zielführende bisher. Und um es zu präzisieren: In AVR Studio: 1) Projekteinstellungen => Toolchain => Linker => General => [x] "Use vprintf library" aktivieren 2) Projekteinstellungen => Toolchain => Linker => Misc => "-lprintf_flt" in dem Eingabefeld für die Linkerflags anhängen.
> Ich zitiere einfach nochmal EAFs Antwort... die einzig zielführende > bisher. Nicht zwangslaeufig. Der Grund warum printf embedded nicht immer vorhanden ist, oder nur rudimentaer ist sein Speicherverbrauch. Kann man manchmal machen, manchmal nicht. Man muss sich halt ueber die Folgen klar sei. > Meine Idee wäre jetzt gewesen den String zu zerlegen und einen Punkt > einzufügen. Es sind immer viele Loesungen denkbar. Ich wuerde einfach nur den String bis zu der Stelle ausgeben wo man einen Punkt haben will, dann den Punkt ausgeben und dann den Rest. Die Sache ist halt die, ein String mit Punkt ist groesser wie einer ohne und alles nach dem Punkt muss verschoben werden. Da muss man wieder Speicher haben damit jonglieren, kopieren, kontrollieren das man Grenzen nicht ueberschreitet. Kann man machen, kann sogar notwendig sein, kann man aber vielleicht auch vermeiden. Olaf
Newbie schrieb: > Meine Idee wäre jetzt gewesen den String zu zerlegen und einen Punkt > einzufügen. In anderen Programmiersprachen kinderleicht (eine Zeile). > Aber hier hänge ich bei jeder zweiten Zeile, weil man in C für jeden > Furz selber eine Funktion schreiben muss. So so. Kann es sein, dass du dich einfach nicht auskennst ? Mehrere einzeilige Lösungen wurden gezeigt. Insgesamt versuchen die Leute hier Losungen zu zeigen, die gleich den richtigen String erzeugen, und nicht später noch Teile davon verschieben.
Gibt es bei dem AVR - C keine Arrays bzw. Float - Arrays ? Einfach ein Array von 0 - 101 nummerieren und dann jedes Element * 0.1 und gleich an einen String daran fügen. Dann hast du es halt in Einerschritten : 0 -> 0.0 1 -> 0.1 2 -> 0.2 3 -> 0.3 4 -> 0.4 5 -> 0.5 usw. Wenn du nur 0.5er Schritte brauchst, kannst du die ja erst mit Modulo rausfiltern und dann an den String setzen.
Newbie schrieb: > Im Listing sind übrigens auch Fehler (frac statt > flag). Nein, da sind keine Fehler. Das "frac" ist was anderes als das "flag".
Wozu braucht man eine String-Nachkommastelle und moechte sie nicht ausgeben ? Das ist doch eher sinnfrei, denn zum Rechnen braucht man's nicht.
Heinz B. schrieb: > Gibt es bei dem AVR - C keine Arrays bzw. Float - Arrays ? > Einfach ein Array von 0 - 101 nummerieren und dann jedes > Element * 0.1 und gleich an einen String daran fügen. OMG! Was für ein Käse! Nur Experten unterwegs . . .
Peter D. schrieb: > sprintf(result, "%4.1f", num*.1); Erfordert allerdings auch float-Support und damit für einen AVR ziemlich viel Flash.
Rolf M. schrieb: > char* toString(char* target, size_t size, int8_t value) Wie wird da sichergestellt, dass in target[4] ein '\0' steht?
Newbie schrieb: > Ich weiß, dass man nicht einfach irgendwas zusammenklatschen sollte, > ohne es zu verstehen. Aber ich brauche das eigentlich nur, um den > prellenden Pollin-Encoder nutzen zu können,... Wieder mal etwas, das ich so nicht mag. Ich halte viel mehr davon, die verschiedenen Dinge nicht zusammenzuklatschen und dann noch um zurühren, sondern sie schön getrennt zu machen. Also zunächst ein Treiber für deinen Drehgeber (incl. ISR), der nichts weiter macht, als beim Betätigen des DG drei bis vier veschiedene Botschaften zu erzeugen: rechtsrum, linksrum, gedrückt und ggf. wiederlosgelassen. Das hat noch gar nix mit irgendwelchen Zahlen zu tun. Sodann ein Programmteil, das auf derartige Botschaften reagiert und z.B. bei rechtsrum oder linksrum eine Zählzelle rauf- oder runterzählt. Das hat zwar mit Zahlen zu tun, aber noch gar nix mit Ausgabekonvertierungen. Sodann ein anderer Programmteil, der aus einer an ihn übergebenen Zahl bzw. einem Zählerstand eine darstellbare Zeichenfolge macht. Das wäre dann der Ausgabekonverter. Schlußendlich irgend eine tatsächliche Ausgabefunktion, z.B. über einen UART, die Zeichenfolgen ausgibt, so daß man sie lesen kann. Teile deine Softwareteile in funktionale Stücke, so daß du die getrennt benutzen und zuvor getrennt testen kannst. Das wäre mein Rat an dich. W.S.
Rolf M. schrieb: > Erfordert allerdings auch float-Support und damit für einen AVR ziemlich > viel Flash. Ein ATmega8 sollte es schon sein (belegt etwa 3,6kB).
Newbie schrieb: > Leider bin ich noch nicht durchgestiegen. Ansätze hätte ich sogar > verschiedene. > Mach das, was W.S. dir empfohlen hat. Wenn dir die Vorgehensweise nicht klar ist, erstelle zuerst ein Flussdiagramm. Die ganzen anderen Vorschläge mit obskuren Stringmanipulationen usw. solltest du vergessen. Zitat: "wenn du es wenigstens einigermaßen klug programmierst, dann wird das Ganze wahrscheinlich viel weniger Maschinencode erzeugen, als wenn du sowas wie sprintf einbindest. Und du hast damit wieder etwas mehr gelernt."
Dirk B. schrieb: > Rolf M. schrieb: >> char* toString(char* target, size_t size, int8_t value) > > Wie wird da sichergestellt, dass in target[4] ein '\0' steht? Darum kümmert sich snprintf() selbstständig.
Rolf M. schrieb: > Dirk B. schrieb: >> Rolf M. schrieb: >>> char* toString(char* target, size_t size, int8_t value) >> >> Wie wird da sichergestellt, dass in target[4] ein '\0' steht? > > Darum kümmert sich snprintf() selbstständig. Wenn ich Zeile 21 in
1 | char buf[] = "snprintf schreibt maximal size Zeichen"; |
ändere, kommt aber ziemlicher Mist raus.
Newbie schrieb: > Meine Variable (int_8) kann den Inhalt 0 bis 100 annehmen. > > Aus 0 bis 100 soll 0.0 bis 10.0 werden (am besten noch rechtsbündig, > aber das ist das kleinere Problem).
1 | extern int_8 i; |
2 | char s[5]=" 0.0"; |
3 | |
4 | if(i<0) {s[0]='<';} // "<0.0" wenn gewünscht im Fehlerfall |
5 | else if(i<100) {s[1]+=i/10; // " X. " |
6 | s[3]+=i%10; // " .x" |
7 | }
|
8 | else {s[0]='1'} // "10.0" |
9 | if(i>100) {s[3]='+*;} // "10.+" wenn gewünscht im Fehlerfall |
Dirk B. schrieb: > Wenn ich Zeile 21 in char buf[] = "snprintf schreibt maximal size > Zeichen"; > ändere, kommt aber ziemlicher Mist raus. Du hast natürlich recht. Das kommt davon, wenn man das morgens schnell noch zwischen Tür und Angel macht. Ich überschreibe ja das \0 von snprintf, also muss man das wieder neu anhängen.
So gehts ohne Speicherverschwendung auch über 100:
1 | char * byte2string1p(char *buf, unsigned char val){ |
2 | char *t; |
3 | t=buf; |
4 | |
5 | *t++=(val<100)?' ': val/100+'0'; //Hunderter |
6 | *t++=val%100/10+'0'; //Zehner |
7 | *t++='.'; |
8 | *t++=val%10+'0'; //Einer |
9 | *t=0: |
10 | |
11 | return t; |
12 | }
|
Jobst Q. schrieb: > ... unsigned char val Das war aber nicht die Fragestellung. Wenn man einem Newbie etwas vorkaut, statt ihm hilft, SELBST etwas zu lernen und eine Lösung zu finden, dann sollte man schon exakt sein.
Dieter R. schrieb: > Jobst Q. schrieb: > >> ... unsigned char val > > Das war aber nicht die Fragestellung. Wenn man einem Newbie etwas > vorkaut, statt ihm hilft, SELBST etwas zu lernen und eine Lösung zu > finden, dann sollte man schon exakt sein. Wer hilft ihm denn hier, "SELBST etwas zu lernen"? Wie soll das gehen ohne Beispiele, wie man es machen kann. 0 bis 100 passt genauso gut in einen unsigned char. Aber warum sollte eine Lösung nur für den Fragesteller passen?
Da muss ich Jobst Q. recht geben. In der Tat lerne ich aus den Beispielen, auch wenn manche nicht zu meinem Problem passen. Um etwas zu verstehen muss ja erst mal etwas funktionierendes da sein. Wenn ich mir nur Befehl für Befehl ansehe, weiß ich immer noch nicht, wie es zusammen funktioniert. Nach dem ich in angemessener Zeit nicht weiterkam, habe ich mein Programm heute in Luna AVR geschrieben. Obwohl ich noch nie damit gearbeitet habe, hatte ich die Software in einem Tag komplett fertig. Die Encoder-Lib funktioniert gut, sogar Pollin-Encoder läuft einwandfrei. Echt cool. Ich wollte mein Projekt einfach fertig bekommen. Die Hardware ist ja auch noch unvollständig und bis Ende März brauche ich sie wirklich. Für C habe ich mir jetzt erst mal ein Buch bestellt: AVR Mikrocontroller - Programmierung in C: Eigene Projekte selbst entwickeln und verstehen von Heimo Gaicher
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.