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!
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...
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....
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.
:-) Ja es sind immer Ganzzahlen also werd ich es mit der Fixed-Point-Arithmetik machen. Vielen Dank mal.
Achso eins noch :-) Wie geh ich denn am besten mit negative Zahlen um? Beispiel: -9/5= -1,8
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...)
Also quasi schauen ob es negativ ist, positiv rechnen und nachher wieder ein Minus "hinschmuggeln" !?
In einer Tabelle nachzuschlagen wäre bei dem kleinen Wertebereich auch zu überlegen, ist auf jeden Fall die schnellste Berechnung.
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
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
@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
Ne als Ergebnis brauch ich schon die Kommastelle. Also als Beispielwert soll 2,2 angezeigt werden.
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.
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
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
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.
> 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.
...
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...)
>>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
> 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...
...
>>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
> 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. ...
>>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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.