Forum: Mikrocontroller und Digitale Elektronik float nach string mit einer nachkommastelle


von Newbie (Gast)


Lesenswert?

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.

von Teo D. (teoderix)


Lesenswert?

sprintf()

von foobar (Gast)


Lesenswert?

sprintf(result, "%d.%d", num/10, num%10);

von Irgend W. (Firma: egal) (irgendwer)


Lesenswert?

Newbie schrieb:
> sprintf(result,"%f",num)

eher: sprintf_s(result, sizeof(resault), "%.1f", num);

Hier unter "precision"
- https://www.cplusplus.com/reference/cstdio/printf/

von Walter (Gast)


Lesenswert?

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.);

von EAF (Gast)


Lesenswert?

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.

von Flo (Gast)


Lesenswert?

Itoa und dann einfach einen Punkt einfügen?

von Mario M. (thelonging)


Lesenswert?

Und die Vornull nicht vergessen.

von Falk B. (falk)


Lesenswert?

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.

von LostInMusic (Gast)


Lesenswert?

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.

von W.S. (Gast)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

Irgend W. schrieb:
> eher: sprintf_s(result, sizeof(resault), "%.1f", num);

Ist blöd, wenn result ein Zeiger ist.

von Newbie (Gast)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

> 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

von Rolf M. (rmagnus)


Lesenswert?

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
von IchNicht (Gast)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

> 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

von MaWin (Gast)


Lesenswert?

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.

von Heinz B. (Firma: Privat) (hbrill)


Lesenswert?

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.

von Adam P. (adamap)


Lesenswert?

Newbie schrieb:
> Im Listing sind übrigens auch Fehler (frac statt
> flag).

Nein, da sind keine Fehler.
Das "frac" ist was anderes als das "flag".

von Purzel H. (hacky)


Lesenswert?

Wozu braucht man eine String-Nachkommastelle und moechte sie nicht 
ausgeben ?
Das ist doch eher sinnfrei, denn zum Rechnen braucht man's nicht.

von Peter D. (peda)


Lesenswert?

Rechtsbündig:
1
sprintf(result, "%4.1f", num*.1);

von Falk B. (falk)


Lesenswert?

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 . . .

von Rolf M. (rmagnus)


Lesenswert?

Peter D. schrieb:
> sprintf(result, "%4.1f", num*.1);

Erfordert allerdings auch float-Support und damit für einen AVR ziemlich 
viel Flash.

von Dirk B. (dirkb2)


Lesenswert?

Rolf M. schrieb:
> char* toString(char* target, size_t size, int8_t value)

Wie wird da sichergestellt, dass in target[4] ein '\0' steht?

von W.S. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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).

von Dieter R. (drei)


Lesenswert?

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."

von Rolf M. (rmagnus)


Lesenswert?

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.

von Dirk B. (dirkb2)


Lesenswert?

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.

von A. S. (Gast)


Lesenswert?

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

von Rolf M. (rmagnus)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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
}

von Dieter R. (drei)


Lesenswert?

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.

von Jobst Q. (joquis)


Lesenswert?

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?

von Newbie (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.