Forum: Mikrocontroller und Digitale Elektronik Kann man diesen C-Code vereinfachen?


von Armin K. (-donald-) Benutzerseite


Lesenswert?

Hallo,

ich habe diesen C-Code, und ich wollte mal fragen ob man diesen 
vielleicht in einen einzeiligen oder einfacheren Code vereinfachen kann.
Natürlich in Anbetracht der Tatsache, dass es sinnvoll ist und nicht im 
Assembler die vielfache Größe wird.

Wenn man die Zahlen 1...4 in die Zahlen 1,2,4,8 wandeln möchte, geht das 
ja mit dem Schiebebefehl gut. Aber hier ist es die andere Richtung. Ich 
habe keine bessere Lösung gefunden.
1
   switch (Input) 
2
    {
3
       case 1:  Output = 1;     break;
4
       case 2:  Output = 2;     break;
5
       case 4:  Output = 3;     break;
6
       case 8:  Output = 4;     break;
7
    };

von Falk B. (falk)


Lesenswert?

Armin K. schrieb:
> vielleicht in einen einzeiligen oder einfacheren Code vereinfachen kann.

Warum?

Ist er schlecht verständlich? Nein.
Ist er zu langsam? Nein.
Belegt er Zuviel Speicher? Nein.

von Markus M. (adrock)


Lesenswert?

Naja, wenn es auf maximale Geschwindigkeit ankäme, könnte man natürlich 
sowas basteln:

static uint8_t inout[9] = { 0, 1, 2, 0, 3, 0, 0, 0, 4 };
Output = inout[Input];

Ansonsten würde ich die Optimierung dem Compiler überlassen.

von Joe F. (easylife)


Lesenswert?

const uint8_t lut[9] = { 0, 1, 2, 0, 3, 0, 0, 0, 4 };

output = lut[input];

von Walter T. (nicolas)


Lesenswert?

Wenn es nur die vier sind: Ist doch okay. Wenn es mehr sind: Du suchst 
den Zweierlogarithmus, was wahrscheinlich letztendlich auf clz() 
hinauslaufen wird.

von mh (Gast)


Lesenswert?

Armin K. schrieb:
> Wenn man die Zahlen 1...4 in die Zahlen 1,2,4,8 wandeln möchte, geht das
> ja mit dem Schiebebefehl gut.

Mit zwei multiplizieren ist lesbarer und damit besser. Daraus folgt, 
dass durch 2 teilen einge gute Option ist.

von Sven B. (scummos)


Lesenswert?

Ich würde hier auch nichts vereinfachen, falls nicht noch mehr Werte 
benötigt werden. Falls doch, würde ich mit einer i=0..n Schleife auf 
alle Bits testen und mir nur was anderes überlegen, wenn das zu langsam 
ist.

"Leicht verständlich" ist immer wichtiger als "kurz" und fast immer 
wichtiger als "schnell", außer es ist klar, dass genau an dieser Stelle 
"schnell" relevant ist.

: Bearbeitet durch User
von Armin K. (-donald-) Benutzerseite


Lesenswert?

Danke für die Hinweise.
Die Loockuptable gefällt mir gut, aber das macht es nicht wirklich 
einfacher zu verstehen.

Armin

von Cyblord -. (cyblord)


Lesenswert?

Armin K. schrieb:
> Danke für die Hinweise.
> Die Loockuptable gefällt mir gut, aber das macht es nicht wirklich
> einfacher zu verstehen.

Du musst dir schon überlegen auf was du Optimieren willst: Auf gute 
Lesbarkeit, auf Geschwindigkeit oder auf Codegröße.
Alles zusammen bekommst du leider nicht.

von Maxim B. (max182)


Lesenswert?

Armin K. schrieb:
> Ich
> habe keine bessere Lösung gefunden.

Hallo,
du kannst mit switch-case und mit if-else probieren und in *.lss 
Assembler-Code ankucken. Du wirst wohl überrascht.

von Wolfgang (Gast)


Lesenswert?

Armin K. schrieb:
> Ich habe keine bessere Lösung gefunden.

Deutlich robuster könntest du den Algorithmus gestalten, indem auch bei 
Zahlen außerhalb der Menge {1.2.4.8} einen definierten Wert als Output 
lieferst.

von Brummbär (Gast)


Lesenswert?

Armin K. schrieb:
> Ich habe keine bessere Lösung gefunden.

Diese Lösung ist für den Leser des Programms die beste Variante.
Sie ist klar verständlich und die Funktion ist auf den ersten Blick 
klar.

Bei einer LUT muss man schon etwas länger nachdenken. Die wäre aber auf 
jeden Fall die bessere Wahl, wenn es sich viele (ich sage mal mehr als 
8) Werte handelt.

von marais (Gast)


Lesenswert?

So würde man das mit >> machen

int convert(int val)
{
  int i=1;
  while (val >>= 1)
    i++;
  return i;
}

Aber wie von vielen hier schon gesagt, ist Deine Lösung mit dem 
switch-Statement viel verständlicher. Oder, um es mit einem der Erfinder 
von C zu sagen: Wenn Du beim Schreiben Deines Programms schon 100% 
Deines Könnens verwendest, wie willst Du das Teil dann jemals debuggen?

von Stefan L. (stefan_l134)


Lesenswert?

Also mathematisch wäre es so am einfachsten:

Output = 2^(Input-1)

von Falk B. (falk)


Lesenswert?

Stefan L. schrieb:
> Also mathematisch wäre es so am einfachsten:
>
> Output = 2^(Input-1)

Kaum. Denn die Funktion ist der 2er Logarithmus.

von Brummbär (Gast)


Lesenswert?

Armin K. schrieb:
> case 8:  Output = 4;     break;

Stefan L. schrieb:
> Output = 2^(Input-1)

2^(8-1) = 128 != 4

von Toni Tester (Gast)


Lesenswert?

Bitte beachte, dass die Lösung mit Lookuptable nicht äquivalent zu 
Deiner Switch-Case-Lösung ist, da diese auch für die 
Zwischen-Eingangswerte gültige Ausgangswerte zurückliefert, aber keine 
Bereichsprüfung des Eingangswerts umfasst und somit z. B. für den 
Eingangswert 9 keinen gültigen Ausgangswert liefert, für den Wert 3 aber 
schon.
Daher müsstest Du zuerst klären, was Du überhaupt erreichen willst, denn 
daraus ergeben sich die möglichen Lösungen.
Moderne Compiler sind nicht doof, so dass dieser ein vollständig 
definiertes Switch-Case-Konstrukt auch selbständig durch eine 
Lookuptable ersetzen kann, wenn alle weiteren Parameter passen, d. h. 
Optimierungsstufe (Du musst dem Compiler sagen, ob er auf 
Geschwindigkeit oder Platzverbrauch optimieren soll; davon hängt die 
beste Lösung ab), Rechnerarchitektur usw.
Am Ende des Tages hilft wohl nur Ausprobieren unter Deinen spezifischen 
Rahmenbedingungen; letztlich sollte die Lösung jedoch primär für 
C-Verständige gut verständlich und nachvollziehbar sein und keine 
"Tricksereien" beinhalten, von denen in zwei Tagen auch Du nicht mehr 
weißt, was Du da genau gemacht hast - wie oben schon steht, wie willst 
Du Deinen Code ordentlich debuggen, wenn Du schon selbst beim Lesen 
Deines eigenen Codes Schwierigkeiten hast und permanent überlegen musst, 
warum Du etwas so gemacht hast und was Du damit eigentlich bezwecken 
wolltest.

von foobar (Gast)


Lesenswert?

Lass es so wie es ist.

Der Vollständigkeit halber: man kann das, insb für größere Zahlen, durch 
binäre Suche machen, z.B. für 32 Bit:
1
   Ouput = ((Input & 0xffff0000) ? 16 : 0)
2
         + ((Input & 0xff00ff00) ? 8 : 0)
3
         + ((Input & 0xf0f0f0f0) ? 4 : 0)
4
         + ((Input & 0xcccccccc) ? 2 : 0)
5
         + ((Input & 0xaaaaaaaa) ? 1 : 0)
6
         + (Input != 0);
Bei deinem Zahlenbereich (1,2,4,8) wäre es:
1
   Output = ((Input & 0xc) ? 2 : 0)
2
          + ((Input & 0xa) ? 1 : 0)
3
          + 1;

von Cyblord -. (cyblord)


Lesenswert?

foobar schrieb:
> Lass es so wie es ist.

Besser wäre es.

>
> Der Vollständigkeit halber: man kann das, insb für größere Zahlen, durch
> binäre Suche machen, z.B. für 32 Bit:
>
1
>    Ouput = ((Input & 0xffff0000) ? 16 : 0)
2
>          + ((Input & 0xff00ff00) ? 8 : 0)
3
>          + ((Input & 0xf0f0f0f0) ? 4 : 0)
4
>          + ((Input & 0xcccccccc) ? 2 : 0)
5
>          + ((Input & 0xaaaaaaaa) ? 1 : 0)
6
>          + (Input != 0);
7
>
> Bei deinem Zahlenbereich (1,2,4,8) wäre es:
>
1
>    Output = ((Input & 0xc) ? 2 : 0)
2
>           + ((Input & 0xa) ? 1 : 0)
3
>           + 1;
4
>

Super lesbar. Auf wie viele WTFs/Sekunde kommst du so in deinen Code 
Reviews?

von Falk B. (falk)


Lesenswert?

https://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung

"Die goldene Regel lautet:

90% der Rechenleistung werden in 10% des Codes verbraucht.

Diese 10% muss man finden und zum richtigen Zeitpunkt optimieren. Der 
Rest muss nur sauber und lesbar geschrieben sein."

von Sebastian S. (amateur)


Lesenswert?

Eine so einfache Abfrage kann man eigentlich nur noch verschlimmbessern.

Schließe mich also Falk an.

von Stefan L. (stefan_l134)


Lesenswert?

Falk B. schrieb:
> Kaum. Denn die Funktion ist der 2er Logarithmus.

Stimmt, ich hab Input und Output verwechselt.

von Armin K. (-donald-) Benutzerseite


Lesenswert?

Danke nochmals für alle Beiträge.
Ich wollte nur sichergehen, dass es nicht etwas sinnvolleres gibt, hätte 
ja sein können dass es etwas besseres gegeben hätte. Ich bin in C nur 
Anfänger.

von Stefan F. (Gast)


Lesenswert?

Kleiner Schwank am Rande:

Falk B. schrieb:
> 90% der Rechenleistung werden in 10% des Codes verbraucht.

Genau deswegen haben Scriptsprachen (auf Servern) zur Implementierung 
der Geschäftslogik durchaus eine Existenzberechtigung. Die harte Arbeit 
(z.B. XML Parsing) machen fertige Bibliotheken, die in einer effizienten 
Sprache implementiert ist.

von A. S. (Gast)


Lesenswert?

Armin K. schrieb:
> Ich wollte nur sichergehen, dass es nicht etwas sinnvolleres gibt

Und nur als Zusammenfassung: Nein, es gibt hier nicht einen Vorschlag, 
der das gleiche macht, wie Dein Code.

von (prx) A. K. (prx)


Lesenswert?

Wenn man es wirklich ganz eilig hat und GCC verwendet, dann gibts noch 
die passende Built-in Function:
int __builtin_ffs (int x)
Returns one plus the index of the least significant 1-bit of x, or if x 
is zero, returns zero.

von zitter_ned_aso (Gast)


Lesenswert?

Armin K. schrieb:
> };

eine switch-Anweisung brauch kein Semikolon am Ende

von zitter_ned_aso (Gast)


Lesenswert?

1
    switch (Input) 
2
    {   
3
       case 1:  
4
       case 2:  Output = Input; break;
5
6
       case 4:  Output = 3;     break;
7
       case 8:  Output = 4;     break;
8
    }

von zitter_ned_aso (Gast)


Lesenswert?

und default-case wird nicht benötigt?

von Bernd K. (prof7bit)


Lesenswert?

In Arm Assembler geht vielleicht was mit RBIT und CLZ.

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.