Forum: Mikrocontroller und Digitale Elektronik PIC16F mit 10 Multiplizieren und Dividieren


von Osccon (Gast)


Lesenswert?

Hallo,
was ist der schnellste weg ohne eine Bibliothek eine 16 Bit Zahl, also 
eine Zahl bestehend aus 2 Registern, mit 10 zu multiplizieren und 
dividieren?
Ich habe schon gesucht und ein paar gute Sachen gefunden, jedoch haben 
die gleich erklärt wie man mit beliebigen Zahlen multipliziert, was hier 
überhaupt nicht nötig ist und ich mir so etwas Speicher sparen will, 
wenn ich das ganze auf mal und durch 10 beschränke.
Vielleicht kennt jemand nen guten Algorithmus dafür?

von Jürgen D. (poster)


Lesenswert?

3 mal Schieben mach div oder mod mit 8.
dann noch zwei mal dazu addieren oder subtrahieren.

Ist in ASM schnell gemacht.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Durch 10 Dividieren (unsigned) geht sinngemäß so
1
(0xcccd * x) >> 19

Die Multiplikation ist 16 * 16 = 32, und >> 19 ist Schieben um 19 Bits 
nach rechts.

von chris (Gast)


Lesenswert?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

chris schrieb:
> http://piclist.com/techref/microchip/math/div/16byconst10-at.htm

Will man sowas wirklich?
 
1
;Subroutine to divide by 10 by repeated subtraction of 10.
 
Bis zu 6553 Schleifendurchläufe?

von Thomas E. (picalic)


Lesenswert?

Servus,

mit 10 multiplizieren geht einfach:
Zahl 1x links-schieben (=Zahl*2) und dies als Zwischenergebnis 
speichern,
dann Zahl noch 2x links-schieben (= dann ist Zahl= Zahl *8) und das 
Zwischenergebnis draufaddieren.

Division /10 ist deutlich komplizierter...

von Carl D. (jcw2)


Lesenswert?

Steht da wirklich "Techref/Microchip".
Wenn man nicht versteht was Johann da macht, dann kann man ja immer noch 
10000er, 1000er und 100er reduzieren, bevor man sich auf die 10er 
stürzt.

von uxxluxxl (Gast)


Lesenswert?

und Johanns * 0xcccd kann man umformen zu 0xcccc +1 und das cccc, also 
0b1100110011001100 kann man auch durch einfaches Schieben und Addieren 
ersetzen.

von Sebastian S. (amateur)


Lesenswert?

Da hat mich mein alter Mathe-Lehrer doch voll verarscht!

Der hat doch glatt behauptet: (Irgendwas * 10) / 10 = "kannst Du Dir 
schenken" bzw. bleibt Irgendwas.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Sebastian S. schrieb:
> Da hat mich mein alter Mathe-Lehrer doch voll verarscht!
>
> Der hat doch glatt behauptet: (Irgendwas * 10) / 10 = "kannst Du Dir
> schenken" bzw. bleibt Irgendwas.

Dann war's der Informatik-Lehrer.

von chris (Gast)


Lesenswert?

Methode1 braucht 347 cyclen .
Es geht schneller, ist klar.

Mul10
Ist (x<<2+x)<<1
Mul25
Ist (x<<1+x)<<3+x
Oder
(Mul10+x+x)<<1+x

Div10
Ist x*25>>8
Wenn
Result *10+10 kleiner gleich X ist,
Inkrementiere Result.

von Christoph db1uq K. (christoph_kessler)


Lesenswert?


von Thomas E. (picalic)


Lesenswert?

Servus,

hab schnell mal Johann's Vorschlag ((0xcccd * x) >> 19) in PIC-ASM 
codiert:
(Laufzeit ca. 170 Zyklen)
1
;---------------- accu = reg16/10
2
;Multiplikation 16x16: akku = (reg * 0xCCCD) >> 16 
3
;(die Multiplikation beginnt mit dem niederwertigen Bit des Multiplikanten)
4
div10
5
  lsrf  reg16+1,W
6
  movwf  accu+1
7
  rrf  reg16,W
8
  movwf  accu
9
10
  movlw  15
11
  movwf  loopcnt
12
  bcf  STATUS,C
13
14
mulLp
15
  rrf  accu+1,F  ;(Carry -> MSB) accu >>= 1
16
  rrf  accu,F
17
18
  bcf  STATUS,C  ;(fall keine Addition: kein Übertrag!)
19
  btfss  loopcnt,1  ;bit 1(loopcnt): 1-1-0-0-1-1-0-0...
20
  goto  skipAdd    ;0: keine Addition
21
22
  movf  reg16,W    ;akku += reg
23
  addwf  accu,F
24
  movf  reg16+1,W
25
  addwfc  accu+1,F
26
27
skipAdd
28
  decfsz  loopcnt,F
29
  goto  mulLp
30
31
;noch 3x rechtsschieben -> gibt >>19
32
  rrf  accu+1,F
33
  rrf  accu,F
34
  lsrf  accu+1,F
35
  rrf  accu,F
36
  lsrf  accu+1,F
37
  rrf  accu,F
38
39
  return

von noop (Gast)


Lesenswert?

Das gibt einen Rekord. Noch nie ein Programm mit so vielen Fehlern wie
dieses gesehen.

von TK (Gast)


Lesenswert?

Div10 habe ich mal über eine Pseudo-Reihenentwicklung ausgeführt:
x/10 <=> x(1/8 - 1/32 + 1/128 - 1/512 + 1/2048)
Kann man hinten noch erweitern - je nachdem, wie genau das Ergebnis 
werden soll.
Hatte in ASM nur wenige Befehle benötigt.

Gruß
TK

von Thomas E. (picalic)


Lesenswert?

noop schrieb:
> Das gibt einen Rekord. Noch nie ein Programm mit so vielen Fehlern
> wie
> dieses gesehen.

Kannst Du die Fehler auch zeigen? Oder wenigstens eine Zahl angeben, mit 
der das Ergebis dieses Programms nicht korrekt ist?

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Thomas E. schrieb:

> hab schnell mal Johann's Vorschlag ((0xcccd * x) >> 19)
> in PIC-ASM codiert: [...]

Der Binärbruch 0b1100110011001100 ist offensichtlich
periodisch. Man müsste also mit drei Schritten auskommen:

t1 = (0xc000 * x)
t2 = t1 + (t1 >> 4)
t3 = t2 + (t2 >> 8)

Der Shift um 8 Stellen kann sicherlich durch Rechnen mit
High- und Lowbyte ersetzt werden.

von Possetitjel (Gast)


Lesenswert?

TK schrieb:

> Div10 habe ich mal über eine Pseudo-Reihenentwicklung ausgeführt:
> x/10 <=> x(1/8 - 1/32 + 1/128 - 1/512 + 1/2048)

Hübsch.
Konstanter Faktor in CSD-Darstellung. Bringt zwar im
vorliegenden Fall keine Ersparnis, schadet aber auch nix.
Sollte man sich trotzdem merken; bei anderen Zahlen kann
eine Einsparung resultieren.

von Thomas E. (picalic)


Lesenswert?

Possetitjel schrieb:
> Der Binärbruch 0b1100110011001100 ist offensichtlich
> periodisch. Man müsste also mit drei Schritten auskommen:
>
> t1 = (0xc000 * x)
> t2 = t1 + (t1 >> 4)
> t3 = t2 + (t2 >> 8)

Das stimmt wohl, nur spart es keine Rechenschritte (im Gegenteil!):
die 16x16 Multiplikation hat man auch hier, dazu kommen dann noch die 
Additionen und Shifts von Zwischenergebnissen...

von Possetitjel (Gast)


Lesenswert?

Thomas E. schrieb:

> Possetitjel schrieb:
>> Der Binärbruch 0b1100110011001100 ist offensichtlich
>> periodisch. Man müsste also mit drei Schritten auskommen:
>>
>> t1 = (0xc000 * x)
>> t2 = t1 + (t1 >> 4)
>> t3 = t2 + (t2 >> 8)
>
> Das stimmt wohl, nur spart es keine Rechenschritte (im
> Gegenteil!): die 16x16 Multiplikation hat man auch hier,
> dazu kommen dann noch die Additionen und Shifts von
> Zwischenergebnissen...

Du willst mir jetzt nicht erzählen, dass Du die vierzehn
Multiplikationen mit Null tatsächlich in der Schleife
ausführen willst?
Ich dachte eigentlich, dass die Idee, auf die Schleife
zu verzichen, offensichtlich ist.

Die Multiplikation mit 0xc ist durch kopieren, schieben,
addieren und (die Zwischensumme) zweimal schieben erledigt.

Zweiter Schritt: Zwischensumme kopieren, viermal schieben,
addieren.

Dritter Schritt: Zwischensumme aus Schritt 2 byteverschoben
addieren.

von Thomas E. (picalic)


Lesenswert?

Possetitjel schrieb:
> Du willst mir jetzt nicht erzählen, dass Du die vierzehn
> Multiplikationen mit Null tatsächlich in der Schleife
> ausführen willst?

Ok, sorry, manchmal sieht man halt den Wald vor lauter Bäumen nicht... 
;)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

TK schrieb:
> Div10 habe ich mal über eine Pseudo-Reihenentwicklung ausgeführt:
> x/10 <=> x(1/8 - 1/32 + 1/128 - 1/512 + 1/2048)
> Kann man hinten noch erweitern - je nachdem, wie genau das Ergebnis
> werden soll.

Der von mir genannte Ausdruch Q = (0xcccd * N) >> 19 hat die Eigenschaft

0 <= N - 10*Q < 10

d.h. N - 10*Q ist gerade der Rest bei der Division durch 10 wie man ihn 
auch von C kennt.

Dein Ausdruck liefert für 8 z.B. 1, zumindest wenn man nicht anfängt mit 
Nachkommastellen zu rechnen (was der von mir genannte Ausdruck ja auch 
implizit tut).  Es kommt also ganz auf die Verwendung des Ergebnisses 
an, wie man vorgeht.

Übrigens ist die Konstante in beiden Fällen i.W. die gleiche, d.h. 
besteht aus dem Bitmuster ...0011... wegen 0.1 = 4/30 - 1/30 und
Bei CCCD und mit 16 Bits gerechnet:
Solche Bitfummeleien hatte ich oben nicht ausgeführt; ich bin davon 
ausgegangen das wäre offensichtlich genug...

von TK (Gast)


Lesenswert?

Ja, wenn ich die 8 vorgebe, dann kommt da eine 1 raus. Wenn man mit
der Ungenauigkeit von 8/10 = 0.8 => aufgerundet 1 leben kann, dann denke 
ich,
dass die ca. 30 Zyklen in ASM doch schon relativ schnell sind. Und wenn 
ich den TO Anfangsthread richtig gelesen habe, ging es auch um schnelle 
DIV10 und MUL10.

Gruß
TK

von Thomas E. (picalic)


Lesenswert?

Kommt halt drauf an, wofür man es braucht. Wenn man /10 (wie vermutlich 
in den meisten Fällen) für eine Binär->Dezimal-Wandlung braucht, kann 
man keine Aufrundung gebrauchen, wenn man tatsächlich einen Wert auf das 
0.1-fache verändern will, ist man mit Rundung evtl. näher dran...

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Johann L. schrieb:

> Bei CCCD und mit 16 Bits gerechnet: [...]

Egal, ob man den Faktor als 1100110011001100 oder
0-0+0-0+0-0+0-0+ darstellt - man benötigt immer
7 Additionen/Subtraktionen für 32 bit.

Wenn man die Periodizität ausnutzt, genügen 3.

von noop (Gast)


Lesenswert?

Thomas E. schrieb:
> picalic

160 ist die erste Zahl, welche nicht passt.
ab 160 gibt es nur 2400 Zahlen, wo das Ergebniss stimmt, der Rest
ist falsch. Cyclen braucht es 196 laut Simulator, wobei einige 
Instruction
umgeschrieben wurden, weil diese im pic10 nicht implementiert sind.

von Peter D. (peda)


Lesenswert?

Thomas E. schrieb:
> Kommt halt drauf an, wofür man es braucht.

Stimmt.
Wenn man ein Aufgabe aus dem Kontext herauslöst, ist sie erheblich 
komplizierter zu lösen.
Z.B. für eine Zahlenausgabe braucht man gar keine Division /10, da ist 
die Subtraktionsmethode am schnellsten und codesparendsten.

von Thomas E. (picalic)


Angehängte Dateien:

Lesenswert?

noop schrieb:
> 160 ist die erste Zahl, welche nicht passt.
> ab 160 gibt es nur 2400 Zahlen, wo das Ergebniss stimmt, der Rest
> ist falsch. Cyclen braucht es 196 laut Simulator, wobei einige
> Instruction
> umgeschrieben wurden, weil diese im pic10 nicht implementiert sind.

Dann wirst Du wohl selbst bei der Umsetzung auf den Base-Family Chip 
einen Fehler eingebaut haben! Mein Programm macht auf einer Enhanced 
Midrange CPU (für das es geschrieben wurde) jedenfalls, was es soll:

von Thomas E. (picalic)


Lesenswert?

@noop:

keine Ahnung, was Du da programmiert hast. Hier ist meine Version für 
Base- und Midrange-Core (Simulation getestet für PIC10F320). Laufzeit 
ist 194 Zyklen (incl."call" und "return"):
1
;---------------- accu = reg16/10
2
;Multiplikation 16x16: akku = (reg * 0xCCCD) >> 16 
3
;(die Multiplikation beginnt mit dem niederwertigen Bit des Multiplikanten)
4
div10
5
  bcf  STATUS,C
6
    rrf    reg16+1,W
7
    movwf    accu+1
8
    rrf    reg16,W
9
    movwf    accu
10
11
    movlw    15
12
    movwf    loopcnt
13
    bcf    STATUS,C
14
15
mulLp
16
    rrf    accu+1,F  ;(Carry -> MSB) accu >>= 1
17
    rrf    accu,F
18
19
    bcf    STATUS,C  ;(falls keine Addition: kein Übertrag!)
20
    btfss    loopcnt,1  ;bit 1(loopcnt): 1-1-0-0-1-1-0-0...
21
    goto    skipAdd    ;0: keine Addition
22
23
    movf    reg16,W    ;akku += reg16
24
    addwf    accu,F
25
  btfsc  STATUS,C
26
  incf  accu+1,F
27
    movf    reg16+1,W
28
    addwf    accu+1,F
29
30
skipAdd
31
    decfsz   loopcnt,F
32
    goto    mulLp
33
34
;noch 3x rechtsschieben -> gibt >>19
35
  rrf    accu+1,F
36
    rrf    accu,F
37
  bcf  STATUS,C
38
    rrf    accu+1,F
39
    rrf    accu,F
40
  bcf  STATUS,C
41
  rrf  accu+1,F
42
    rrf  accu,F
43
    return

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.