Hallo, programmiere gerade einen Attiny2313 in Assembler. Habe auch schon fast alles hinbekommen was ich mir vorgestellt habe. Nur jetzt stehe ich aufm Schlauch. Möchte eine Ganzzahl in 2 Registern durch eine fixe Kommazahl teilen. Ganzzahlen: 1 bis 4000 Feste Kommazahl: 0,8789 Also z.B. 1800/0,8789=2048 Beim Ergebnis benötige ich keine Nachkommastellen. Ich hoffe mir kann jemand einen Denkanstoß geben. Vielen Dank schonmal
ich programmier selbst kein asm, aber in C würde ich mit 225 multiplizieren(ungefähr 0.8798*256) und anschließend durch 256 Teilen (>>8)
Hi Multiplikation mit 1/0,8789. Multiplikationsroutinen findest du bei AppNotes von Atmel. MfG Spess
oh, ich seh grade du hast nen tiny, der hat gar keinen mul befehl...
Peter Stuckl schrieb: > Hallo, > > programmiere gerade einen Attiny2313 in Assembler. > > Habe auch schon fast alles hinbekommen was ich mir vorgestellt habe. > > Nur jetzt stehe ich aufm Schlauch. > > Möchte eine Ganzzahl in 2 Registern durch eine fixe Kommazahl teilen. Dividieren ist etwas aufwendig. Ich würde es vermeiden, indem ich mit dem Reziprokwert der fixen Zahl multipliziere. > > Ganzzahlen: 1 bis 4000 > > Feste Kommazahl: 0,8789 > > Also z.B. 1800/0,8789=2048 Da bietet sich an, mit 291 zu multiplizieren und das untere Byte wegzuwerfen, um durch 256 zu teilen. Also 1800 mal 291 (ergibt drei Bytes) Nur die beiden oberen Bytes beachten (ersetzt eine Division durch 256) Denn: Der Reziprokwert von 0,8789 ist 1,1377858. Dies wird mit 256 multipliziert (Bruch erweitert) und ergibt 291,27... Der Fehler beträgt knapp 3/256, also gut 0,1%. Die Erweiterung mit 256 wird dann durch Weglassen des unteren Bytes kompensiert. Wenn es sein muss, kann man zuvor noch 128 addieren um das Ergebnis zu runden. Das wäre dann: 1800 x 291 (= 523800) / 256 = 2046 > > Beim Ergebnis benötige ich keine Nachkommastellen. Die wären bei Bedarf aber da. > > Ich hoffe mir kann jemand einen Denkanstoß geben. Die Multiplikation mit 291 erreichst Du übrigens durch 9 mal Shiften (über 3 Bytes) und 3 Additionen (je 3 Bytes). ;-) > > Vielen Dank schonmal
Such dir nen Bruch der möglichst nahe an deiner Zahl liegt. Dann multiplizierst du ganzzahlig deine Zahl mit dem Zähler (auf Überlaufe achten -> Zahlenbereich), und dann dividerst du durch den Nenner. Ideal wäre ein Bruch, der im Nenner eine 2er-Potenz hat, dann kann man das Dividieren durch Schieben ersetzen.
Viel dank für die schnellen und zahlreichen Antworten. 1800*289/254= 2048 Das kommt dem ganzen noch am nächsten denke ich. Was mich aber stört sind die 3 Bytes (1800*289). Aber das bekomme ich wohl hin.
@ Peter Stuckl (Gast) >1800*289/254= 2048 Das hat aber KEINER erzählt. Eher 1 / 0,8789 = 1,1377859 = 291 / 256 Macht einen Fehler von 0,93%, Hmmm. >Das kommt dem ganzen noch am nächsten denke ich. Ich würde auf 1165 / 1024 gehen, macht 0,08% Fehler für gerade mal zwei Bit mehr. MFG Falk
Da ich kleine kompakte Umrechnungen öfter brauche, habe ich mal mein Verfahren als Code-Schnipsel angefügt. Die Multiplikationsroutine kann man immer brauchen, das Ergebnis ist nicht exakt, aber optimal genau.
1 | ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
2 | |
3 | .def Tmp0 = R16 |
4 | .def Tmp1 = R17 |
5 | .def Tmp2 = R18 |
6 | .def Tmp3 = R19 |
7 | .def Tmp4 = R20 |
8 | .def Tmp5 = R21 |
9 | .def Tmp6 = R22 |
10 | .def Tmp7 = R23 |
11 | .def Tmp8 = R24 |
12 | .def Tmp9 = R25 |
13 | |
14 | ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
15 | |
16 | ; K = 1 / 0,8789 |
17 | ; |
18 | ; Konstante muss < 1 sein, also: |
19 | ; |
20 | ; A / 0,8789 = A * 1,137786 |
21 | : = A * 2 * 0,568893 |
22 | ; = A * 2 * (0,568893 * 65536) / 65536 |
23 | ; = A * 2 * 37283 / 65536 |
24 | |
25 | |
26 | ldi Tmp0, low(1800) |
27 | ldi Tmp1, high(1800) ; A |
28 | |
29 | lsl Tmp0 |
30 | rol Tmp1 ; A = 2 * A |
31 | |
32 | push Tmp2 |
33 | push Tmp3 ; Register sichern |
34 | |
35 | ldi Tmp2, low(37283) |
36 | ldi Tmp3, high(37283) |
37 | rcall mult_01_23 ; A = A * 2 * 37283 |
38 | |
39 | mov Tmp0, Tmp2 |
40 | mov Tmp1, Tmp3 ; A = A * 2 * 37283 / 65536 = A / 0,8789 |
41 | |
42 | pop Tmp3 |
43 | pop Tmp2 ; Register wiederherstellen |
44 | |
45 | |
46 | ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
47 | |
48 | mult_01_23: ; IN: Tmp1-0 = A, Tmp3-2 = B |
49 | push Tmp4 ; OUT: Tmp3-2-1-0 = A x B |
50 | push Tmp5 |
51 | push Tmp6 |
52 | push Tmp7 |
53 | push Tmp8 |
54 | push Tmp9 |
55 | clr Tmp4 |
56 | clr Tmp5 |
57 | clr Tmp6 |
58 | clr Tmp7 |
59 | clr Tmp8 |
60 | clr Tmp9 |
61 | |
62 | mult0: |
63 | lsr Tmp1 |
64 | ror Tmp0 |
65 | brcc mult1 |
66 | add Tmp6, Tmp2 |
67 | adc Tmp7, Tmp3 |
68 | adc Tmp8, Tmp4 |
69 | adc Tmp9, Tmp5 |
70 | mult1: |
71 | lsl Tmp2 |
72 | rol Tmp3 |
73 | rol Tmp4 |
74 | rol Tmp5 |
75 | |
76 | tst Tmp0 |
77 | brne mult0 |
78 | tst Tmp1 |
79 | brne mult0 |
80 | |
81 | mov Tmp3, Tmp9 |
82 | mov Tmp2, Tmp8 |
83 | mov Tmp1, Tmp7 |
84 | mov Tmp0, Tmp6 |
85 | |
86 | pop Tmp9 |
87 | pop Tmp8 |
88 | pop Tmp7 |
89 | pop Tmp6 |
90 | pop Tmp5 |
91 | pop Tmp4 |
92 | ret |
93 | |
94 | ; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
Guten Morgen @Ralli das läuft optimal mit dein Vorschlag! Hätte nie gedacht, dass man das so hinbekommt!
Hi >; K = 1 / 0,8789 >; >; Konstante muss < 1 sein, also: >; >; A / 0,8789 = A * 1,137786 >: = A 2 0,568893 >; = A 2 (0,568893 * 65536) / 65536 >; = A 2 37283 / 65536 Die Berechnung kann der Assembler2 auch allein:
1 | ldi r18,Low(Q15(FRAC(1/0.8789))+1) |
2 | ldi r19,High(Q15(FRAC(1/0.8789))+1)+$80 |
MfG Spess
0,8789r*65536=74565,93469109113 hex 1.2345 binär 1.0010001101000101 das genaue Ergebnis für 4000 ist 4000/0,8789=4551,1434747 d.h. Du kopierst die Zahl in ein Summierregister (alles 16bit-Operationen) shiftest die Zahl um 3 nach rechts und addierst ins Summierregister shiftest die Zahl um weitere 4 nach rechts und addierst ins Register shiftest die Zahl um weitere 1 nach rechts und addierst ins Register shiftest die Zahl um weitere 2 nach rechts und addierst ins Register shiftest die Zahl um weitere 4 nach rechts und addierst ins Register *) fertig - Ergebnis steht im Summierregister das sind 2*(1+4+5+2+3)=30 Befehle *) diese Zeile bringt nur etwas, wenn wie unten beschrieben vorher nach links geschoben wurde dann sind es 2*(1+4+5+2+3+5)=40 Befehle Wenn's etwas schneller, dafür ungenauer sein darf (/0,8767123287 entspricht *1,140625): d.h. Du kopierst die Zahl in ein Summierregister (alles 16bit-Operationen) shiftest die Zahl um 3 nach rechts und addierst ins Summierregister shiftest die Zahl um 3 nach rechts und addierst ins Summierregister 4000 wird so zu 4000+500+62=4562 so sind es 2*(1+4+4)=18 Befehle Wenns genauer sein soll: Der Wert 4000 passt in 12 Bit, also zuerst um 1-4 Bits nach links schieben, dann wie oben beschrieben rechnen und Ergebnis wieder um 1-4 Bits nach rechts schieben. Das kann man noch optimieren: Zahl ins Register kopieren Zahl um 3 nach links schieben und ins Register addieren Originalzahl um 4 nach rechts schieben und ins Register addieren weiter wie oben zum Schluss Ergebnis wieder um 3 nach rechts Das Runden bringt auch mehr Genauigkeit: Einfach das "Nachkommabit" auf das Ergebnis addieren. also beim letzten Beispiel nur noch das Carry addieren (adc #0), da sich das Nachkommabit nach dem letzten shr im Carry befindet. Falls noch Fragen, bitte fragen! Mich würde interessieren, wie Du es gelöst hast.
Kann es sein, dass Du "nach links shiften" meinst? Denn nach rechts ist ja Division durch 2, da bleibt am Ende nix übrig. ;-(
Nein, ich meine schon nach rechts schieben. Zum Schluss soll ja nichts mehr übrigbleiben. Nochmal zur Verdeutlichung ein Beispiel: 1. 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 4000 2000 1000 500 250 125 62 31 15 7 3 1 4000+ 500+ 31+15+ 3 =4549 Und das Beispiel mit erhöhter Genauigkeit durch "shl 3": 1. 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 32000 16000 8000 4000 2000 1000 500 250 125 62 31 15 7 3 1 32000+ 4000+ 250+125+ 31+ 1=36407 36407/8=4550,875 Runden ergibt 4551, da das Nachkommabit 1 ist. Wieso hättest Du auf Linksschieben getippt?
eProfi schrieb: > Nein, ich meine schon nach rechts schieben. Zum Schluss soll ja nichts > mehr übrigbleiben. > Nochmal zur Verdeutlichung ein Beispiel: > 1. 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 > 4000 2000 1000 500 250 125 62 31 15 7 3 1 > 4000+ 500+ 31+15+ 3 =4549 > > Und das Beispiel mit erhöhter Genauigkeit durch "shl 3": > 1. 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 > 32000 16000 8000 4000 2000 1000 500 250 125 62 31 15 7 3 1 > 32000+ 4000+ 250+125+ 31+ 1=36407 > 36407/8=4550,875 Runden ergibt 4551, da das Nachkommabit 1 ist. > > > Wieso hättest Du auf Linksschieben getippt? Sorry, ich bin von meinem Vorschlag mit der Multiplikation mit 291 und Pseudo-Division durch 256 (unteres Byte wegwerfen) ausgegangen: Beitrag "Re: AVR Assembler Ganzzahl durch Kommazahl" Dies wäre mit Linksschieben und Addieren recht einfach zu erledigen. 291 ist ja 0b100100011. Das sind 9 Bit mit insgesamt 4 Einsern, also 9 Links-Shifts mit 3 Additionen (da wo die Einser stehen). Aber was soll's, es sind genügend verschiedene Vorschläge gemacht worden, ich erwarte daher nicht, dass mein Vorschlag umgesetzt wird. ;-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.