Warum auch immer (der Hintergrund ist hier nicht gefragt und deshalb
bitte keine "Sinnfrage") stellt sich mir die Frage der
Ausführungsgeschwindigkeit für eine BIN2BCD Funktion. Mein eigentliches
Programm in dem ein Konverter benötigt wird, habe ich verändert, sodass
dort die Geschwindigkeit keine Rolle mehr spielt.
Meine Funktionen sind diese hier (und werden momentan in einem AVR
System verwendet). Ganz gezielt die Division- und Modulodivision sind
mir hier ein Dorn im Auge, weil ich genau weiß, dass AVR keine
Hardwaredivision besitzt:
1
uint32_t bin2bcd(uint32_t bin, uint8_t diganz)
2
{
3
int bcd, dig, i;
4
5
bcd= 0;
6
7
for (i= 0; i < diganz; i++)
8
{
9
dig = bin % 10;
10
bin = bin / 10;
11
bcd = (dig << (i << 2)) | bcd;
12
}
13
return bcd;
14
}
15
16
uint8_t byte2bcd(uint8_t bin)
17
{
18
uint8_t bcd;
19
20
bcd= bin % 10;
21
bcd |= (bin / 10) << 4;
22
return bcd;
23
}
Also, geht das besser, im Besonderen die Konvertierfunktion für einen
8-Bitwert (und außer, dass man das auch in ein define packen kann und
sich so den CALL und den RET sparen kann)?
Mir geht es hier vor allem um die Logik und nicht darum, etwas mittels
Inline-Assembler zu machen.
Willsd du binäre Darstellung als eine Zahl direkt abspeichern? Alle 8
Bits (oder gar mehr)?
Das passt in uint8_t nicht rein. Auch nicht in eine int-Variable (beim
AVR).
Am PC könnte man mit auch so vorgehen:
1
#include<stdlib.h>
2
#include<stdio.h>
3
4
int
5
nibble2bcd(intbin)
6
{
7
switch(bin){
8
case0:
9
return0;
10
case1:
11
return1;
12
case10:
13
return2;
14
case11:
15
return3;
16
case100:
17
return4;
18
case101:
19
return5;
20
case110:
21
return6;
22
case111:
23
return7;
24
case1000:
25
return8;
26
case1001:
27
return9;
28
}
29
30
return0;
31
}
32
33
int
34
byte2bcd(intbin)
35
{
36
returnnibble2bcd(bin/10000)*10+
37
nibble2bcd(bin%10000);
38
}
39
40
int
41
main(void)
42
{
43
intx=10001001;// BCD: 89
44
45
printf("bcd:%d\n",byte2bcd(x));
46
47
returnEXIT_SUCCESS;
48
}
aber am PC passt sowas wie
1
intx=10001001;
auch in den Wertebeich.
Vielleicht lieber als Array abspeichern?
ein Array vielleicht schrieb:> int x = 10001001;
Ach so, und mit einer 0 darf die Zahl dann auch nicht anfangen, sonst
würde das als eine Oktalzahl interpretiert;
Ralph S. schrieb:> Warum auch immer
Mir erschließt sich der Zweck deiner Funktion nicht. Immerhin soll
diese ein Argument unsigned long bekommen und ebenso ein Ergebnis
unsigned long liefern.
Bedenke mal, daß sowas wie BCD und Konsorten lediglich Darstellungen
sind für den menschlichen Gebrauch, wo bei genauerer Betrachtung eine
Anzahl von Bitkonstellationen im Argument keinen Sinn ergeben.
Wenn das eine Vorbereitung für eine E/A-Konvertierung werden soll, dann
arbeite mit einem Feld von Textzeichen.
Und nochwas: Wenn du schon Modulo und Division direkt nebeneinander
verwendest, dann ist das vom Rechenaufwand so etwa das Doppelte, als
wenn du nur einmal 'ldiv' verwendest.
W.S.
Ralph S. schrieb:> bcd = (dig << (i << 2)) | bcd;
Das i<<2 ist typische premature Optimization.
Der Code wäre deutlich verständlicher, wenn dort stehen würde
> bcd = (dig << (i*4)) | bcd
Dann wüsste man sofort, dass positionsabhängig jeweils 4 bit verschoben
wird.
So muss man sich erst mal überlegen "Wofür ist diese Berechnung?"
Der Compiler optimiert das ohnehin zum selben Code.
Hab etwas Vertrauen in den Compiler:
https://www.youtube.com/watch?v=w0sz5WbS5AM&t=25s
Für bin 2 bcd gibt es seit 50 Jahren das addiere 3 Verfahren oder so.
Nur schieben und addieren.
Es spielt aber heute keine Rolle mehr, da Tabellen oder mal und geteilt
kaum etwas kosten.
Ralph S. schrieb:> Mir geht es hier vor allem um die Logik und nicht darum, etwas mittels> Inline-Assembler zu machen.
Aber gerade das wäre doch logisch...
Einen zu den Anforderungen passenden Algorithmus wählen muss man
natürlich unabhängig von der Programmiersprache.
Aber praktisch jeder der in Frage kommenden läßt sich dann (speziell auf
einem AVR8) in Asm deutlich bis sehr deutlich effizienter implementieren
als in "native" C.
c-hater schrieb:> Aber praktisch jeder der in Frage kommenden läßt sich dann (speziell auf> einem AVR8) in Asm deutlich bis sehr deutlich effizienter implementieren> als in "native" C.
Fragezeichen angemeldet :-)
Auf dem AVR mag das u.U. noch stimmen.
Auf komplizierteren Prozessoren ist es sehr wahrscheinlich, dass der
Compiler mehr Tricks drauf hat und besser Assembler kann als ein
normalsterblicher Programmierer.
Tilo R. schrieb:> Auf dem AVR mag das u.U. noch stimmen.
Da stimmt das sogar ganz sicher. Und der Performance-Vorteil fällt hier
ziemlich beeindruckend aus.
> Auf komplizierteren Prozessoren ist es sehr wahrscheinlich, dass der> Compiler mehr Tricks drauf hat und besser Assembler kann als ein> normalsterblicher Programmierer.
Was soll ein normalsterblicher Programmierer sein? Einer, der de facto
nur C kann? Ja klar, so einer hat natürlich kaum Chancen, besser als der
Compiler zu sein.
Dass das so ist, liegt allerdings zu einem gut Teil daran, dass der
Compiler wiederum große Teile der Mathematik aus vorgefertigten, bereits
in Assembler geschriebenen Macros zusammenbaut. Ein tiefer Blick in die
Quelltexte der Compiler zeigt dir das. Das gilt für praktisch jede
Zielarchitektur, die sich einer einigermaßen breiten Nutzung erfreut.
c-hater schrieb:> Dass das so ist, liegt allerdings zu einem gut Teil daran, dass der> Compiler wiederum große Teile der Mathematik aus vorgefertigten, bereits> in Assembler geschriebenen Macros zusammenbaut. Ein tiefer Blick in die> Quelltexte der Compiler zeigt dir das. Das gilt für praktisch jede> Zielarchitektur, die sich einer einigermaßen breiten Nutzung erfreut.
c-hater, da sind wir ganz einer Meinung.
Es ist wie beim Schach: Ein Schachcomputer hat eine riesige
Eröffnungsbibliothek, während selbst erfahrene Schachspieler nur eine
überschaubare Anzahl und Varianten im Kopf haben.
Auf populären Architekturen gewinnt der Compiler, so wie der Computer
gegen normale Schachspieler.
Auf der Randgruppenarchitektur AVR hast du sicher oft recht. Allein wie
oft der avrgcc unnötig Register auf den Stack pusht...
Pluspunkt Compiler ist, dass er neben der Assembler-Bibliothek auf
logischer Ebene argumentieren/optimieren kann, und so ggf. manche
Berechnungen gar nicht machen muss, durch Lookups ersetzen kann etc.
Und das über den lokalen Kontext hinaus, ggf. auch durch den Einsatz von
Heuristiken.
Heuristiken sind lustigerweise auch, warum JIT-Compiler schneller sein
können als statisch kompilierter Code: Der JIT-Compiler kann in den
ersten (langsamen) Durchläufen Statistiken erstellen, um danach auf die
tatsächlich verarbeiteten Daten hin optimierten Maschinencode zu
erstellen.
Selbst aus Horror-Sprachen, wie dem schwach typisierten JavaScript, wo
ein Objekt ein beliebig erweiterbares Dictionary ist, kommt so
irgendwann (einigermaßen) performanter Maschinencode raus. Aus dem
teuren Member-Lookup wird ein typisierter Speicherzugriff mit fixem
Offset, so effizient wie ein C-Struct.
Nur so lässt sich der Web-Bloat überhaupt ertragen. Ohne sowas wäre
Webassembly nicht denkbar.
Aber lass uns das nicht zum Flamewar werden.
Was ich sagen will ist: Die Welt ist nicht schwarz und weiß. In vielen
Fällen lohnt es sich nicht über Optimierungen nachzudenken. (Effizienten
Algorithmus mal vorausgesetzt.)
In manchen (imho seltenen) Fällen ist Assembler ungeschlagen.
ein Array vielleicht schrieb:> Am PC könnte man mit auch so vorgehen:
alles sehr seltsam, ich verstehe nicht was du mit dem Programm erreichen
willst?
Eine Umwandlung in BCD ist es mit Sicherheit nicht
Walter schrieb:> alles sehr seltsam, ich verstehe nicht was du mit dem Programm erreichen> willst?
Das geht mir auch so.
Ich wüßte keinerlei Anwendung, wo man gepackte BCD-Nibbles benötigt.
Vielleicht ganz zu Anfang der Computerära hat mal jemand 7-Segment
Decoder an eine CPU angeschlossen. Seitdem geistert diese komische
Packroutine durch die Lehrbeispiele.
Speziell auf dem AVR geht zur Ausgabe die Subtraktionsmethode mit
Zehnerpotenzen am schnellsten. Aber das wurde ja schon bis zum Abwinken
hier im Forum diskutiert.
In der Programmierpraxis benutzt man einfach sprintf().
W.S. schrieb:> Und nochwas: Wenn du schon Modulo und Division direkt nebeneinander> verwendest, dann ist das vom Rechenaufwand so etwa das Doppelte, als> wenn du nur einmal 'ldiv' verwendest.
Das dürfte der Compiler schon passend optimieren. Nicht umsonst heißen
die entsprechenden Funktionen irgendwas mit _divmod, die werden nicht
öfter aufgerufen als nötig.
Zur ursprünglichen Frage: Ich vermute (ohne in den generierten
Assembler-Code geschaut zu haben), das langsamste daran dürfte der
variable Shift um (4*i) sein.
Es könnte sich lohnen, das mit einem char[] zu machen und entweder
hinterher zu packen (wenn überhaupt nötig - die Frage, was du mit dem
zurückgegebenen int denn machen willst, wurde ja schon gestellt) oder
zwei Stellen pro Schleifendurchlauf zu verarbeiten. Hätte auch den
(vermutlich theoretischen) Vorteil, dass dir dein Rückgabewert nicht
schon bei 100.000.000 überläuft, wenn du Eingaben bis 2^32-1, also
~4.000.000.000, erlaubst.
Für den 8-Bit-Wert ist auch der Speicher-Mehraufwand für eine
Lookup-Table handhabbar - muss ja nur bis 99 gehen, mehr kannst du als
gepacktes BCD in einem Byte eh nicht zurückgeben (dein Code macht für
Eingaben über 99 auch Blödsinn, da würde ich auf AVR ohne Betriebssystem
auch einen *Lese*zugriff außerhalb der Arraygrenzen akzeptieren).
MfG, Arno
ein Array vielleicht schrieb:> Ach so, und mit einer 0 darf die Zahl dann auch nicht anfangen, sonst> würde das als eine Oktalzahl interpretiert;
??
Was für einen Compiler verwendest du denn?
Ich arbeite selten unter C, aber in PAscal kannst du klar sagen es
Oktalm, Binär oder sonst was sein soll
c-hater schrieb:>> Compiler mehr Tricks drauf hat und besser Assembler kann als ein>> normalsterblicher Programmierer.>> Was soll ein normalsterblicher Programmierer sein? Einer, der de facto> nur C kann? Ja klar, so einer hat natürlich kaum Chancen, besser als der> Compiler zu sein.
Das ist einer der in C/C++ programmiert und dann normal stirbt. (eine
Aussage über sein Können ist nicht möglich, sicher ist nur dass
ausschliesslich Gottheiten C/C++ so gut beherrschen können dass kein
Mensch was daran zu meckern hat)
Das ist anders bei c-hater: er ist dazu verdammt als Zombie bis in die
Ewigkeit sein Assembler-Evangelismus zu verbreiten. Sterben wird nie
dürfen. Leider.
pro Richtige Hochsprachen schrieb:> c-hater schrieb:>>> Compiler mehr Tricks drauf hat und besser Assembler kann als ein>>> normalsterblicher Programmierer.>>>> Was soll ein normalsterblicher Programmierer sein? Einer, der de facto>> nur C kann? Ja klar, so einer hat natürlich kaum Chancen, besser als der>> Compiler zu sein.>> Das ist einer der in C/C++ programmiert und dann normal stirbt. (eine> Aussage über sein Können ist nicht möglich,...
Nun ja, dir wohl nicht. Sowas ist eine Frage deines Horizontes. Aber
wenn du das so siehst, dann ist nach deiner Auffassung ein
'normalsterblicher' Programmierer einer, der außer C oder C++ nichts in
seinem Leben gesehen hat. So einer hat natürlich keine Chance, irgend
etwas außer C bzw. C++ zu kennen und zu können.
Andere Leute mit weiterem Horizont sehen das ganz anders.
W.S.
A. S. schrieb:> A. S. schrieb:>> addiere 3 Verfahren>> Scheint im Englischen so zu heißen (nie gehört)>> https://en.wikipedia.org/wiki/Double_dabble
so, nachdem ich hier jetzt alles mal gelesen habe, werde ich das
Verfahren hier mal genauer ansehen.
Wie zu erwarten war gabs wieder mal Anfeindungen gegeneinander, Posts
natuerlich nach dem Sinn des ganzen und warum man das ueberhaupt will
(es soll ja auch RTC Chips geben, die mit BCD Zahlen gefuettert werden
wollen).
Und natuerlich wieder, was fuer schlechte Programmierer denn alle sind
(ausgenommen die eigene Person).
W.S. schrieb:> Und nochwas: Wenn du schon Modulo und Division direkt nebeneinander> verwendest, dann ist das vom Rechenaufwand so etwa das Doppelte, als> wenn du nur einmal 'ldiv' verwendest.
:-) das war so ziemlich das erste (sogar noch vor deinem Post) was
geaendert wurde...
-----------------------------
Fuer alle anderen die die Frage nach dem Sinn stellen: das ist eine Uhr,
die die Uhrzeit auf einer charliegeplexten LED Anzeige mit 20 Leds die
Uhrzeit ausgibt. Die LED sind so angeordnet, dass sie im BCD Code
abgelesen werden koennen.
Ralph S. schrieb:> Die LED sind so angeordnet, dass sie im BCD Code> abgelesen werden koennen.
Ich nehme mal an, daß du das mit einem kleinen Padauk-µC machen willst.
Deshalb der Versuch, die verschiedenen Dinge wie Ausgabekonverter und
Display-Ansteuerung miteinander zu vermengen - in der Hoffnung, dadurch
ein wenig Code zu sparen.
Naja, damit erklärt sich so einiges.
W.S.
Man könnte das ganze ja auch im C Modus ausprobieren..
vom Sprintf Ergebnis einfach die 4Bits des Asciicodes direkt in den
Quell ULong zurückschreiben.
W.S. schrieb:> Ich nehme mal an, daß du das mit einem kleinen Padauk-µC machen willst.
Nein, in diesem Falle ein ATtiny44 an den auch noch ein DCf77 Modul
angeschlossen ist... Und läuft. Für den Padauk werde ich das dann
realisieren, wenn ich für diesen eine genaue Zeitbasis zustande bekomme.
Ein DCf will ich dort nicht hernehmen.
Grundsätzlich geht's mir ja darum , ultrabilliges Bastelzeugs zu nehmen
und daraus etwas zu machen. Seit der Pandemie (und den neueren
Zollgesetzen) habe ich da keinen wirklichen Spaß mehr daran, weil es
ultrabillig nicht mehr gibt.
Ralph S. schrieb:> ein, in diesem Falle ein ATtiny44
bei so wenig speicher würde ich das mit ultoa versuchen, das sollte
eigentlich nochmal Speicher sparen.. nur muss man sich dann um führende
Nullen etc. selbst kümmern.
... unfassbar!!! Es ging um schneller (und effizienter) und nicht
kleiner im Sourcecode....
Umwege über Ascii-Zeichen oder gar Strings sollen da Zielführend sein?
Im Leben nicht...
Am ehesten ldiv, weil in der rückgebenden Struktur die Modulo und
normale Division schon enthalten die ist.
Ralph S. schrieb:> ... unfassbar!!! Es ging um schneller (und effizienter) und nicht> kleiner im Sourcecode....
Meine Idee funktioniert auch garnicht, man müsste danach verdrahten und
bei nullen gibts auch ein Problem. Sry, ups.
Außerdem gibt es die beste Lösung ausdiskutiert anno 2005 in einem Atmel
Forum. Da ging es auch um einen Attiny.
Das dcf kommt doch sowieso in Häppchen, die ports sind 8bit.. da
erschliesst sich mir der Sinn nicht ganz wieso man mit uint32 daher
kommt.