Beiträge von goloMAK im Thema „Dividieren - here we go again!“

    wesentlich ist, daß der Computer auf keinen Fall mit echt gebrochenen Werten rechnen kann *) - allenfalls mit sehr großen ganzen Zahlen!

    Da lag der Knackpunkt: Ich wollte gefühlsmäßig die Zahl immer noch in Vorkomma- und Nachkommateil aufspalten, obwohl das durch die Skalierung eigentlich gar keinen Sinn mehr ergab.

    Auf jeden Fall echt gut, diese Skalierung. :)

    Noch kurz:

    P.S. Du hast in Bitte melde dich an, um diesen Link zu sehen. am Anfang der Routine LDA #0 vergessen. Den Befehl kannst Du nicht einfach weglassen, sonst rechnet die Routine falsch!

    Da hatte ich einfach den ganzen INIT-Block zur Vereinfachung weggelassen. dvd und dvd+1 ($fb und $fc in deinem Code) müssen ja auch vorher 0 gesetzt werden, oder?

    Auf jeden Fall sieht das komplette Programm bei mir jetzt so aus und funktioniert auch:

    Nochmal danke für die guten Tipps!

    Muss ich wirklich das ganze Rundungsbyte zusammennudeln, obwohl mich nur das höchste Bit interessiert?

    Nein, das ist nicht nötig. Im Akku steht nach Ausführung der Routine der Rest und Du mußt nur nachprüfen, ob der Rest >= Divisor/2 ist.

    Hm... guter Tipp, aber im Akku steht zum Schluss eine 4 (auch in deinem Screenshot), der Rest von 23/7 ist aber 2. :gruebel

    Ich glaube, der Rest steht nach dem 8. Durchgang der Schleife im Akku, oder? Denn dann steht ja auch das ganzzahlige Ergebnis fest.

    Jetzt ist mir noch aufgefallen, dass meine 24-Bit-Näherung für 23/7 doch nicht optimal war.

    $24 $49 $03 hat eine größere Abweichung als $25 $49 $03.

    Ich hatte einfach abgebrochen, als der Rest kleiner war als 2-16, aber tatsächlich hätte man aufrunden und 2-16 noch setzen müssen.

    Jetzt ist die Frage, wie setzt man das programmtechnisch um? :)

    Ich habe die etwas pummelige Lösung gewählt und ein viertes Ergebnisbyte eingeführt. Das höchste Bit dieses Zusatzbytes entscheidet dann, ob aufgerundet wird. So hier:

    Das klappt zwar so, aber ich werde mal wieder das Gefühl nicht los, dass das auch eleganter gehen müsste. Muss ich wirklich das ganze Rundungsbyte zusammennudeln, obwohl mich nur das höchste Bit interessiert?

    Was wären vielleicht noch ganz andere Ansätze?

    23x65536, ich hab' das im Original-Beitrag gerade noch korrigieren können. Das "x" steht hier für Multiplikation.

    Ah... jetzt rappelt's! :) Der Dividend fängt in der Gesamtbetrachtung bei Bit 16 an, so gesehen muss sein Wert deshalb auch mit 2^16 malgenommen werden.

    Damit ist dann die 23 in 8:16-Fixpunktdarstellung und in Little Endian eben $00 $00 $17 - so wie Du das oben im Bild siehst, eingespeichert in $FB, $FC und $FD.

    Den Screenshot finde ich sehr gut. Faszinierend, dass die ganze Routine mit kleiner Demo auf einen einzigen VC20-Schirm passt!

    Danke, Mike! Da habe ich was zum Tüfteln.

    Eine Frage:

    Der Dividend ist dann 22x65536 - also, in 8:16-Darstellung.

    Der Dividend ist in meinem Beispiel ja 23. Warum entspricht das jetzt 22x65536 und nicht 23x0? (Ich weiß auch nicht wirklich, wofür das "x" steht?)

    Und:

    P.P.S. wolltest Du pi annähern? Dann wäre allerdings 22/7 der Bruch, den Du ausrechnen wolltest. ;)

    Oh, nein, diese zufällige Nähe ist mir gar nicht aufgefallen. Ich wollte allerdings einen Divisor haben, bei dem ein schön krummes Ergebnis mit langer Periodenlänge herauskommt, und so kam ich auf 7. Der Dividend sollte nicht zu groß sein, damit es nicht so viele Durchläufe gibt und ich die Bitverschiebungen leichter nachvollziehen kann.

    Und so habe ich dann - beinahe - Pi neu entdeckt. :)

    Wieso wird das Ergebnis auf 3 Bytes aufgeteilt? Bei 8 Durchläufen, kann es nur max 8 Bit umfassen.

    8 Bit für die Ganzzahl, dann noch einmal 16 Bit für die Nachkommastellen (im 2. Loop).

    Aber da war natürlich noch Luft drin, die Nachkomma-Bytes hatten im 1. Loop nichts verloren. Meine momentan kürzeste Fassung sieht so aus:

    Wenn man mehr "Dezimalstellen" im Ergebnis haben möchte, muss man den "Rest" weiter zerteilen und das die Schleife etwa auf 16 oder 24 Bit ausweiten. Dann ergäbe das einen Sinn.

    Beide Schleifen zusammen ergeben ja 24 Bit. Mein zentrales Anliegen war es, die beiden Schleifen zu einer zusammenzufassen, die gleich alle 24 Bits abfrühstückt. Ich bin aber unsicher, ob das überhaupt naheliegend/sinnvoll wäre.

    Aber zu den Optimierungen an sich:

    * X als Bitzähler kann man sich sparen, wenn man im Result-Byte res einfach Bit 0 zu Beginn setzt und dann dieses Bit dann nach dem 8 ROL beim Carry angelangt ist, dann ist das Carry-Flag gestetzt und wir haben 8 Durchläufte.

    * tmp kann man sich auch sparen, wenn man gleich den Akku dafür verwendet.

    Cool, das hört sich beides interessant an. :thumbup: Muss ich demnächst ausprobieren/einbauen.

    (Anbei noch die obige Fassung als PRG.)

    Dividieren in Assembler, immer wieder ein schönes Thema!

    Vor allem, wenn eine Kommazahl herauskommt.

    Konkret soll in meinem Beispiel 23/7 gerechnet werden. Laut Taschenrechner ~= 3,285714.

    Als binäre Näherung habe ich ermittelt: 0000 0011 0100 1001 0010 0100

    Das heißt 2^1+2^0+2^(-2)+...+2^(-14) ~= 3.285706

    Das Ziel ist also, dass genau diese drei Bytes (Hex $03 $49 $24) hintereinander im Speicher liegen.

    Mein Code unten erfüllt diesen Zweck zwar, aber er ist doch recht redundant. Irgendwie finde ich nicht den richtigen Übergang, nachdem das ganzzahlige Ergebnis vorliegt. Ich habe immer das Gefühl, es müsste sich alles mit einer einheitlichen Schleife erledigen lassen.

    Was habe ich übersehen? Verbesserungsvorschläge willkommen!