mikrocontroller.net

Forum: Compiler & IDEs C 4stellige Zahl aufsplitten


Autor: noXe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo alle zusammen.

Sitze gerade vor folgendem Problem:
Bin dabei eine 4Stellige sieben Segment Anzeige aufzubauen und möchte
dort die Zahlen anzeigen lassen, die von einem Atmega8 kommen sollen.
Mein Problem ist jetzt, wie splitte ich eine Zahl z.B. 9876 ( in C ) so
auf, dass die „9“ in einer Variablen steckt, die „8“ in einer anderen
usw?

PS: Ja, geggoogelt hab ich auch, hab nur nichts Sinnvolles gefunden.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Durch sukzessive Modulo / Division.
z.B.:

unsigned char stelle[4];
unsigned int wert;

for(i = 0; i < 4; i++)
{
    stelle[i] = wert % 10;
    wert /= 10;
}

Dann stehen in stelle[0...3] die einzelnen Dezimalstellen, und zwar die
Einser in stelle[0], die Zehner in stelle[1] usw...

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
uint8_t digits[5];

void splitit(int num)
{
  div_t x;
  int8_t i;

  memset(digits, 0, sizeof digits);
  for (i = 4; i >= 0; i--) {
    x = div(num, 10);
    digits[i] = x.rem;
    num = x.quot;
  }
}

Autor: noXe (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja ich glaube damit lässt sich was anfangen. danke euch. werde es mal
sofort ausprobieren.

Autor: Walter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine andere Möglich keit ist die Zahl in einen buffer zu sprinten,
und die jeweilge Zahl aus dem String zu nutzen

zahl = 123;
buffer[46];
sprintf(buffer,"%i",zahl);

stelle = buffer[x]-0x30

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Naja, ein sprintf ist aber in dem Zusammenhang echt mit Kanonen auf
Spatzen geschossen. Da kann man noch eher itoa nehmen. Das bläht das
Programm nicht so auf und das Ergebnis ist das selbe...

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Walter:
Nur als kleines Beispiel: Aus dem hier

unsigned char string[6];
unsigned int wert = 12345;
sprintf(string, "%i", wert);

macht der AVR-GCC-C-Compiler satte 1922 Bytes Code (Optimierung -Os).

Das hier

unsigned char string[6];
unsigned int wert = 12345;
itoa(wert, string, 10);

gibt kompakte 312 Bytes. Ich glaub, da fiele die Wahl nicht schwer,
gell?

Aber die ganz oben genannten Methoden sind sicher noch kleiner...

Autor: Walter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
290 Bytes Code.
Zahl ist in 4 ASCII Zeichen umgewandelt.
Wenn du kein ASCII haben willst, ziehe 30hex von jeder ab.

unsigned char lookup [17] = "0123456789ABCDEF";
unsigned char buffer[5];

int main(void)
{
 unsigned int zahl;

 buffer[0] = lookup[(zahl)      & 0x000F];
 buffer[1] = lookup[(zahl >>4 ) & 0x000F];
 buffer[2] = lookup[(zahl >>8 ) & 0x000F];
 buffer[3] = lookup[(zahl >>12) & 0x000F];

return 0;
}

Autor: Walter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oops, der Zahl fehlt noch dein Beispielwert.

unsigned char lookup [17] = "0123456789ABCDEF";
unsigned char buffer[5];

int main(void)
{
 unsigned int zahl = 9847;

 buffer[3] = lookup[(zahl)      & 0x000F];
 buffer[2] = lookup[(zahl >>4 ) & 0x000F];
 buffer[1] = lookup[(zahl >>8 ) & 0x000F];
 buffer[0] = lookup[(zahl >>12) & 0x000F];

 return 0;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das macht aber leider etwas anderes.
Du kreierst das Equivalent zu
itoa(wert, string, 16);
Gesucht war aber
itoa(wert, string, 10);

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau, in dem Beispiel würde nicht 9847 ausgegeben, sondern das
Hexadezimal-Äquivalent 2677. Und das ist ein klitzekleiner Unterschied.
Genau das ist ja das Problem, wenn man von einem Prozessor
Dezimalstellen bekommen will: Die Division durch 10 ist eben nicht so
schön einfach durch Bitschubsereien machbar und macht deshalb mehr Code
erforderlich.

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Na klar, die Codesammlung ist dazu da, damit da garantiert keiner
reinguckt :-(

Man oh man.

O.k. das Beispiel ist fürn 8051, aber C ist ja portabel.

Anbei die Version umgeschrieben für Konstanten im Flash fürn WINAVR.

Ist dann ganze 58 Byte groß.

Also Leute wirklich.


Peter

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm.

> void outint( u16 val, u8 * result )
> {
>   u8 d, i;
>   u16 tval;
>
>   for( i = 4; i; i-- ){
>     tval = pgm_read_word(TEST+i-1);    // get test value
>     for( d = 0; val >= tval; val -= tval )
>       d++;          // count subtractions
>     *result = d;        // store digit

      *result = d + '0';

>     result++;
>   }
>   *result = val;        // store ones
> }

macht noch ein paar Bytes zusätzlich :-)

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oder aber:

    for( d = '0'; val >= tval; val -= tval )
      d++;          // count subtractions
    *result = d;        // store digit

d fängt nicht mehr bei 0, sondern bei '0' an zu zählen.
Damit sollte das wieder auf 58 Bytes rauslaufen.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Karl Heinz,

ASCII nützt bei 7-Segment aber nichts.

Außerdem hast Du die Einer vergessen.

Ansonsten siehe Codesammlung.


Peter

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> @Karl Heinz,
>
> ASCII nützt bei 7-Segment aber nichts.

<nach oben scroll>

Schäm.
Ja, das hatte ich übersehen.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meine Variante braucht übrigens (einschließlich der
Bibliotheksroutine, die ja u. U. sowieso gebraucht wird)
132 bytes.  Wäre die Frage, was von beiden schneller ist,
und was einem dann dabei wichtiger ist.

Autor: Supersofti1970 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich tendiere zur ersten Version.

Autor: Netzpolizei (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Benutzung der Funktion div() hat halt den Vorteil, daß die
Divisionen nicht alle doppelt gemacht werden müssen. Das spart eine
Menge Zeit.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> daß die Divisionen nicht alle doppelt gemacht werden müssen.

Was glaubst Du denn, was die Funktion div() macht? Du musst die
Division und das Modulo vielleicht nicht explizit hinschreiben, aber
div() macht intern mit Sicherheit auch nix anderes... Vom
Programmieraufwand ist das glaub ich kein wirklicher Unterschied.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Vom Programmieraufwand ist das glaub ich kein
> wirklicher Unterschied.

Dann solltest du dir mal den Divisionsalgorithmus angucken.
Der Rest fällt bei der ganzzahligen Division immer zugleich
mit dem Quotienten mit an.  Erinnere dich einfach an deine
Division natürlicher Zahlen in der Grundschule: dort war das
auch schon so.

Daher hat es Sinn, div() zu benutzen, wenn man sowohl den
Quotienten als auch den Rest ohnehin benötigt.  (Sonst gäbe
es diese Funktion sicher gar nicht erst.)  Da die Division die
teuerste Grundrechenoperation ist, spart man knapp die Hälfte
an Zeit (und vermutlich einiges mehr gegenüber Peter's "poor
man division" mittels fortlaufender Subtraktion).

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg:
Hast natürlich recht. Mit Programmieraufwand meinte ich aber eigentlich
den Schreibaufwand im Programm. Dass der Code dann größer und evtl.
langsamer wird ist die andere Sache...

Autor: Peter Dannegger (peda)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg,

"Da die Division die teuerste Grundrechenoperation ist, spart man
knapp die Hälfte an Zeit (und vermutlich einiges mehr gegenüber
Peter's "poor man division" mittels fortlaufender Subtraktion)."


Nö.

Die 16Bit-Divisonsschleife wird 16* durchlaufen je Digit, die
Subtraktion aber nur max 9* (worst case).

Die optimierte Assemblersubtraktion über Null hinweg ist nochmal
wesentlich schneller (siehe Anhang).

Spielt aber für menschliche Ausgaben keinerlei Rolle.


Peter

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.