Forum: Compiler & IDEs C 4stellige Zahl aufsplitten


von noXe (Gast)


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.

von johnny.m (Gast)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

1
uint8_t digits[5];
2
3
void splitit(int num)
4
{
5
  div_t x;
6
  int8_t i;
7
8
  memset(digits, 0, sizeof digits);
9
  for (i = 4; i >= 0; i--) {
10
    x = div(num, 10);
11
    digits[i] = x.rem;
12
    num = x.quot;
13
  }
14
}

von noXe (Gast)


Lesenswert?

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

von Walter (Gast)


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

von johnny.m (Gast)


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

von johnny.m (Gast)


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

von Walter (Gast)


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;
}

von Walter (Gast)


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;
}

von Karl H. (kbuchegg)


Lesenswert?

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

von johnny.m (Gast)


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.

von Peter D. (peda)


Angehängte Dateien:

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

von Karl heinz B. (kbucheg)


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

von Karl heinz B. (kbucheg)


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.

von Peter D. (peda)


Lesenswert?

@Karl Heinz,

ASCII nützt bei 7-Segment aber nichts.

Außerdem hast Du die Einer vergessen.

Ansonsten siehe Codesammlung.


Peter

von Karl heinz B. (kbucheg)


Lesenswert?

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

<nach oben scroll>

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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.

von Supersofti1970 (Gast)


Lesenswert?

Ich tendiere zur ersten Version.

von Netzpolizei (Gast)


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.

von johnny.m (Gast)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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

von johnny.m (Gast)


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

von Peter D. (peda)


Angehängte Dateien:

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

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.