Forum: Mikrocontroller und Digitale Elektronik Kommastelle


von Thorsten (Gast)


Lesenswert?

Hallo zusammen
Mit meinem ATMega8515 führe ich in Assembler einige Rechnungen durch.
Unter anderem muss ich eine Zahl durch 5 teilen.
Die Zahl kann zwischen -20 und +20 liegen und ist ganzzahlig.

Kann mir jemand einen Tipp geben wie man das teilen durch 5 am
geschicktesten realisiert?
Wie wird denn dann die Nachkommastelle welche entsteht berücksichtit?

Beispiel:
9/5=1,8
Das Ergebnis möchte ich nun gerne weiterverwenden bzw. auf einem
Display anzeigen.
Wie geh ich mit diesen Kommazahlen um?

Vielen Dank schonmal für eure Hilfe!

von MasterFX (Gast)


Lesenswert?


von Jan M. (mueschel)


Lesenswert?

Am besten geht das mit fixedpoint-Format:
(x*10)/5
Dann hast du im Ergebnis die letzte Stelle als Nachkommastelle, du
musst also nur das Komma an der richtigen Stelle einfügen bei der
Ausgabe.
Das kannst du natürlich gerade kürzen zu
x*2
was nochmal eine schöne Ersparnis bringt, da das nur ein reiner
linksshift ist.
Ergo: Ein einziger Takt benötigt. Für eine echte Division bist du
locker 50 los...

von Rahul (Gast)


Lesenswert?

Sind -20 und +20 durchweg ganze Zahlen?
Dann könnte man einfach die ganzzahlige Division mit Rest durchführen.
Der Rest kann dann nur Werte zwischen 0 und 4 annehmen (Nachkommastelle
wären dann 0/5=0, 1/5= 0,2, 2/5=0,4, 3/5=0,6 und 4/5=0,8).
Die müsste man bei der Anzeige nur noch "hinzuaddieren" / mit
anzeigen.
Bei Kommazahlen müsste man die Zahl entsprechend der gewünschten
Nachkommastellen erweitern und dann das Ergebnis wieder um den Faktor
teilen....

von Karl heinz B. (kbucheg)


Lesenswert?

Die einfache Antwort lautet:
Besorg dir eine Fliesskomm-Arithmetik-Bibliothek und
dein Problem ist gelöst. Dafür öffnet sich ein neuer
Sack von Problemen.

Die pragmatische Lösung lautet:
Gar nicht. Die meisten µC unterstützen von Haus aus
nur Ganzzahlen. Da es bei deinem µC so ist, wirst du mit
den ganzen Zahlen alleine auskommen müssen.

Die praktische Antwort lautet:
Wieviele Nachkommastellen willst du den?
Eine. Oh das ist leicht

 ( 9 * 10 ) / 5 = 18

und bei der Ausgabe schmuggelst du zwischen Zeher- und Einer-
stelle ein Komma hinein.

Im Ernst: Das Zauberwort heist meistens Fixed-Point-Arithmetik
Die Idee dahinter ist einfach alle Berechnungen mit einem
ganzzahligen Vielfachen zu machen. Wie im obigen Beispiel:
Alles mal 10 und sich merken, dass da ein Komma vor der
letzten Stelle sein muss.
Die Idee ist an sich nicht neu, du machst das mit Sicherheit
schon seit Jahren so. Rechnen wir doch mal:

  2 Euro 50  +  3 Euro 20

also  2.50 + 3.20  ergibt 5.70

Das kann man so rechnen, muss man aber nicht. Man kann anstelle
von Euros ganz einfach auch in Cent rechnen

    250 + 320  ergibt 570

und bei der Ausgabe schmuggelt man ein Komma vor die Zehnerstelle

    5 Euro 70

Wenn 2 Nachkommastellen nicht reichen, dann geht man halt
auf 1/10 Cent oder 1/100 Cent als Basiseinheit.

Das funktioniert solange gut, solange alles in einen der vorhandenen
Ganzzahltypen passt und die Wertebereiche alle in etwa gleich
sind: Wenn ich die Entfernung zum Mars in Atomdurchmessern
ausdrücken möchte, dann wird das nicht wirklich gut funktionieren.

von Thorsten (Gast)


Lesenswert?

:-)

Ja es sind immer Ganzzahlen also werd ich es mit der
Fixed-Point-Arithmetik machen.

Vielen Dank mal.

von Thorsten (Gast)


Lesenswert?

Achso eins noch :-)

Wie geh ich denn am besten mit negative Zahlen um?
Beispiel:
-9/5= -1,8

von Rahul (Gast)


Lesenswert?

Mein Vorschlag:
Prüfen ob die Zahl negativ ist, wenn ja, dann Zweierkomplement bilden
und merken, dass es sich um eine negative Zahl handelt.
Dann die Division berechnen.
Und dann bei Bedarf wieder zurückwandeln.

Bestimmt findet man sonst auch irgendwo "Berechnungsregeln" für
sowas... (Andreas Roth hat das "Mikrocontroller Applikations
Kochbuch" mit solchen Sachen gefüllt...)

von Thorsten (Gast)


Lesenswert?

Also quasi schauen ob es negativ ist, positiv rechnen und nachher wieder
ein Minus "hinschmuggeln" !?

von Rahul (Gast)


Lesenswert?

genau

von Rahul (Gast)


Lesenswert?

Kann aber auch sein, dass das noch einfacher geht...

von Christoph Kessler (db1uq) (Gast)


Lesenswert?

In einer Tabelle nachzuschlagen wäre bei dem kleinen Wertebereich auch
zu überlegen, ist auf jeden Fall die schnellste Berechnung.

von Hagen R. (hagen)


Lesenswert?

Die Frage ist ob du im Resultat den Bruchteil/Kommastelle auch benötigst
und anzeigen möchtest, oder ob du wiederum eine gerundete Ganzzahl haben
möchtest. Falls zweites zutrifft dann einfach (X + (5-1)) / 5 rechnen.

Gruß Hagen

von arc (Gast)


Lesenswert?

n/5 = n * 0.2, 0.2 passend skalieren z.B. 20
ergibt für -20 -> -400 = -4.0 und für z.B. 9 -> 180 = 1.8
Falls man keine Multiplikation hat x = n <<= 4 + n << = 2

von Hagen R. (hagen)


Lesenswert?

@arc, ja das ist nichts anderes als Fixkomma Rechnung ;)
Würdest du mit 2^x skalieren (statt 100) dann stände dieses Festkomma
an x'ter Stelle, so steht es an 10^2'ter Stelle.

Gruß Hagen

von Thorsten (Gast)


Lesenswert?

Ne als Ergebnis brauch ich schon die Kommastelle.
Also als Beispielwert soll 2,2 angezeigt werden.

von arc (Gast)


Lesenswert?

Wie soll aus einer geraden Zahl (2^x) und 1/5 eine ganze Zahl werden.
Bsp. 0.2 * 256 = 51.2 und diese 51.2 müssten immernoch * 10 genommen
werden um "genau" zu sein.

von Hagen R. (hagen)


Lesenswert?

Gegenfrage wie willst du 10*100 / 3 auf diese Weise exakt berechnen ?

Fakt ist: ob du mit 100 skalierst oder mit 2^x spielt mathematisch
gesehen keine Rolle. Beides ist eine Skalierung zum Zwecke einer
Fixpoint Berechnung, nur die Zahlen Basis ist unterschiedlich.

Klar, eine Skalierung mit 100 hätte den Vorteil das wir damit besser
umgehen können und in der eventuellen späteren Anzeige einiges
einfacher würde. Aber das ist kein Argument pro Basis 10^x denn intern
sind alle Zahlen sowieso 2'er Potenzen. Wir müssen also für die
Anzeige in beiden Fällen eine Umrechnung in einen String vornehmen. Ob
diese Umrechnung nun einen String formatiert zur Basis 10 oder 2 oder
16 (HEX) erledigt ist dabei irrelevant.

Die Berechnungen einer Fixpoint Zahl zur Basis 2 sind dagegen
wesentlich einfacher. Zb. eine Division durch 2^x ist immer ein
Rechtsshift mit anschließender Fixpoint Korrektur. Bei der Basis 10
müssen wir diese Operation immer mit vollständigen Divisonen und
Multiplikationen durchführen. Die Basis 2 ist also weit besser geeigent
für schnelleren Code.

Das math. Resultat ist aber bei beiden identisch, mal abgeshen von der
Wertmäßigen Auflösung der Berechnungen, sprich den Genauigkeiten.

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

Beipsiel:

Basis ist 2^8 = 256. Also steht unser Fixpoint immer bei Bit 8, das
unterste Byte = LSB stellt also die Nachkomastellen dar, die Bruchteile
von x * 1/256.

Eine Anzeige dieser Zahl kann nun sehr effizient Downscalen. Man
betrachtet nur die MSB's ansich und hat den Vorkommateil als Ganzzahl.
Die Nachkommastellen stehen im LSB drinnen. Runden ist ganz einfach
indem man checkt ob LSB >= 128 ist und dann MSB +1 rechnet.

Wie aber zu Basis 10^x ?

Wir wollen runden und müssen erstmal unsere Zahl durch 10^x dividieren
um den Vorkommateil und die Nachkommastellen als Rest zu bekommen.

Also tricksen wir indem wir vor dieser Division exakt 10^x / 2 auf
unsere Zahl addieren und erst danach durch 10^x dividieren. In jedem
Falle müssen wir aufwendig dividieren und das ist jua bekanntlich auf
dem AVR ein zeitraubender Prozess.

Wenn wir mit 10^2 = 100 skalieren dann haben wir eine AGenauigkeit in
den Nachkomastellen von 100, sprich 100 verschiedene Werte kann die
Nachkomastelle annehmen, sprich 1/100'tel

Skalieren wie mit 2^8 = 256 dann haben wir eine Genauigkeit von 256
Werten sprich 1/256'tel. Zusätzlich ab den Vorteil das wir direkt die
Kommastelle im binärcode unserer Zahlen haben, also nicht nur
mathematisch betrachtet sondern auch technisch gesehen. Wir können nun
das LSB als Nachkommastelle ansprechen.

Nunja: ob Fixpoint oder Fließkomma, man benutzt idealerweise immer ein
Skalierung mit 2^x.

Gruß Hagen

von Detlef _. (detlef_a)


Lesenswert?

Hi,

die von Jan und Karl Heinz vorgeschlagene Lösung ist ne
Milchmädchenrechnung. Die Lösung von 9/5 war demnach 9*2=18 und nen
Komma bei der Ausgabe reinschmuggeln. Wer macht denn die Ausgabe,
printf() ?! Zitat Hagen: 'denn intern sind alle Zahlen sowieso 2'er
Potenzen'. Die '18' oder hexadecimal 0x12 muß dann von printf()
durch 10 geteilt werden, damit ist die Division drin und das ganze
printf-Zeuchs.

So machen:
for(k=0;k<=20;k++) {
 m=k;i=0;
 while(m>4){m-=5;i++;}
 printf("%.1f %c.%c\n",k/5.0,i+'0',(m<<1)+'0');
}

Cheers
Detlef

PS: Das ist eigentlich keine Milchmädchenrechnung, sondern eine
Managerrechnung, weil die Division an printf() delegiert wird, und
delegieren ist ja die Schlüsselqualifikation des Managers.

von Hagen R. (hagen)


Lesenswert?

;)

von Hannes L. (hannes)


Lesenswert?

> Wer macht denn die Ausgabe, printf()

Hmmm... - Im ersten Posting war aber von Assembler die Rede, da gibt es
kein printf()

Meine Eigenbau-LCD-Routinen haben sehr wohl die Möglichkeit, ein Komma
an einer bestimmbaren Position (n. Stelle von rechts) einzuschmuggeln.
Somit bietet sich das Skalieren um Zehnerpotenzen an.

...

von Jan M. (mueschel)


Lesenswert?

Und selbst wenn man printf verwenden will, geht das einfach. Man darf
mit dem "vorkauen" für den Compiler nur nicht einfach mittendrin
aufhören, sondern das ganze konsequent durchziehen.
Zahl mit itoa() in einen String umwandeln, dort zwischen erstem und
zweiten Zeichen ein Komma einfügen, Ausgabe mit printf.
Wieviel Zeit das dann wirklich noch einspart, bleibt dahingestellt,
aber zum Einen war danach nicht gefragt und zum Anderen widersprechen
sich die beiden Themen "Laufzeitoptimierung" und "Ausgabe mit
printf".
Klar, printf ist ein mächtiges Werkzeug, aber in 95% der Fälle völlig
übertrieben. (Hat mal einer 'nen Presslufthammer? Müsste ma' schnell
zwei Eier für den Kuchen aufschlagen...)

von Detlef _. (detlef_a)


Lesenswert?

>>Hmmm... - Im ersten Posting war aber von Assembler die Rede, da >>gibt
es kein printf()

genau, deswegen bringt die Rechnung 9*2=18=0x12 nix. Zur 'händischen'
Darstellung ohne Benutzung von printf() muß du 0x12 durch 10 teilen.

>> Zahl mit itoa() in einen String umwandeln,

Das Dividieren durch 10 muß iota auch machen.

Bei dem gewünschten Zahlenbereich geht das mit dem Beispielcode.

Cheers
Detlef

von Hannes L. (hannes)


Lesenswert?

> Das Dividieren durch 10 muß iota auch machen.

Wenn man binäre Zahlen als ASCII-String mit Ziffern in
Dezimalschreibweise ausgeben will, dann wird man um das Teilen durch 10
(100, 1000...) sowiso nicht herum kommen. Das war aber sicherlich nicht
das Problem...

...

von Detlef _. (detlef_a)


Lesenswert?

>>Das war aber sicherlich nicht das Problem...

Nee, das Problem war durch 5 zu teilen, jetzt vermeidet man das und
muss durch 10 teilen, Thorsten will das Ergebnis anzeigen!

Wird Zeit, daß die Gentechniker aus den Pantoffeln kommen und
standartmäßig 8 Finger an jeder Hand möglich machen um dem
Dezimalsystem den Todesstoß zu versetzen.

Cheers
Detlef

von Hannes L. (hannes)


Lesenswert?

> Nee, das Problem war durch 5 zu teilen, jetzt vermeidet man das und
> muss durch 10 teilen, Thorsten will das Ergebnis anzeigen!

Man muss (in der Ausgaberoutine) sowiso durch 10 teilen, wenn man die
(binäre) Zahl dezimal anzeigen will. Das Teilen durch 5 war eine
zusätzliche Berechnung, die nichts mit der sowiso erforderlichen
Integer-ASCII-(String)-Konvertierung zu tun hat. Diese lässt sich im
konkreten Fall durch das Skalieren mit 10 einsparen, was durch Anzeigen
der letzten Stelle als dezimale Nachkommastelle kompensiert wird. Dazu
muss allerdings die (ASM-) Ausgaberoutine über die Möglichkeit des
Einfügens eines Dezimaltrennzeichens (Punkt, Komma) an eine
definierbare Position verfügen. Da man in ASM seinen Code selbst
schreibt und nicht auf irgendwelche Bibliotheken bzw. Funktionen
zurückgreift, ist das aber kein Problem.

...

von Hagen R. (hagen)


Lesenswert?

>>Wird Zeit, daß die Gentechniker aus den Pantoffeln kommen und
>>standartmäßig 8 Finger an jeder Hand möglich machen um dem
>>Dezimalsystem den Todesstoß zu versetzen.

Du wirst es kaum glauben aber 30 Finger sind noch besser. Denn das ist
2*3*5 und enthält somit alle kleinen Faktoren. Es ist direkt kompatibel
zu jedem Zahlensystem <= 30 das sich aus diesen Faktoren ergäbe, also
2,3,4,5,6,8,9,10,12,15,16,18,20,usw. usw.

Und ein guter Freund von mir (Mathematiker und Statistiker) rechnet
sogar real mit solchen Zahlen im Kopf, verrückt.

Sorry für OT.

Gruß Hagen

von arc (Gast)


Lesenswert?

Fractional/Fixpoint-Format gab's schon mal...
z.B. http://www.mikrocontroller.net/forum/read-10-353356.html
und ist in diesem Fall absoluter Overkill, da nur eine Nachkommastelle
rauskommt. Eine Multiplikation mit 2 würde also reichen (zum runden
+5).

"Es gibt nicht nur Vorschlaghämmer"

p.s. diese Zahlensysteme sind eigentlich nur sinnvoll, wenn man
Primzahlen im Kopf bestimmen will, die ökonomischte (ganzzahlige) Basis
ist 3, ansonsten e.

von Thorsten (Gast)


Lesenswert?

hmm da hab ich ja ne ganz nette Diskussion angeregt :-)
Vielen Dank jedenfalls für eure Hilfe!

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.