Hallo! Ich möchte einen 16 bit int mit einer Kommazahl multiplizieren und als Resultat ein 16 bit int erhalten das bei Bedarf natürlich auf oder abgerundet werden muss. Beispiel: 1000*1,2=1200 Wie stelle ich das am geschicktesten an? Ich vermute das das Ganze um eine Kommastelle (oder die Anzahl benötigter Kommastellen) nach links geschoben wird um damit zu rechnen aber ich denke *10 und später /10 ist eben nicht geschickt. Ich bin mir sicher im Forum wimmelt es vor Beiträgen zu diesem Thema allerdings gelingt es mir nicht den richtigen Suchbegriff zu finden. Vielen Dank schon mal!
Attila Ciftci schrieb: > Beispiel: 1000*1,2=1200 kann könnte es noch mit float rechnen, dafür musst du nur das , durch ein . ersetzen. oder man teilt es einfach auf x = 1000 x += (1000/10) * 12
ich würde eher um 2er-Potenzen schieben, wenn dein chip keine hardwaredivision kann. Also 1000 * 1,2 = 1000 * 307 / 256 = 1000 * 78643 / 65536 dafür musst du nur einige zusätzliche Bits zur Verfügung stellen (16bit -> 32bit -> 16 bit) die Divisionen durch 2er Potenzen sind integer-schiebeoperationen
Max H. schrieb: > Peter II schrieb: >> x = 1000 >> x += (1000/10) * 12 > x ist dann am Ende 2200. danke. Dann so >> x = 1000 >> x += (1000/10) * 2
Danke Peter! Für das Benutzen von float gibt es halt immer böse Schelte ;-) hier im Forum! Deine Lösung mit dem Aufteilen gefällt mir sehr gut!
rava schrieb: > ich würde eher um 2er-Potenzen schieben, wenn dein chip keine > hardwaredivision kann. Also > > 1000 * 1,2 = 1000 * 307 / 256 = 1000 * 78643 / 65536 > > dafür musst du nur einige zusätzliche Bits zur Verfügung stellen (16bit > -> 32bit -> 16 bit) > > die Divisionen durch 2er Potenzen sind integer-schiebeoperationen das ist dann aber wesentlich langsamer als x = 1000 x += (1000/10) * 2 ist nur eine Addition und ein shift.
Danke rava! Gibt es einen Artikel zu Deinem Lösungsvorschlag? Weil so wie Du es beschreibst verstehe ich es nicht auf Anhieb.
Peter II schrieb: > das ist dann aber wesentlich langsamer als > > x = 1000 > x += (1000/10) * 2 > > ist nur eine Addition und ein shift. Das ist aber sicherlich nicht der Regelfall. Denn in der Regel lautet die Aufgabe ja nicht 1000 zu behandelen, sondern x. Wodurch die Division durch 10 relevant wird.
Attila Ciftci schrieb: > Danke rava! Gibt es einen Artikel zu Deinem Lösungsvorschlag? Weil so > wie Du es beschreibst verstehe ich es nicht auf Anhieb. Eigentlich sehr einfach, wenn man es mal verstanden hat. Ausgangspunkt ist die Division. Divisionen sind teurer als Multiplikationen. D.h. man möchte es dem µC einfach machen und durch etwas dividieren, was er gut kann. Und das sind nun mal 2-er Potenzen. Durch 2, 4, 8, 16, ... zu dividieren ist leicht. Alle anderen Fälle sind schwer. Wenn man es dann noch besonders leicht machen möchte, dann dividieret man durch 256, oder 65536. Denn das eine ist 2 hoch 8 und damit die Bitbreite eines Byte und das andere ist 2 hoch 16 und damit die Beitbreite eines uint16_t. In diesen Fällen ist die Division besonders einfach, denn dann muss man noch nicht einmal Bitschieben, sondern lässt einfach 1 oder 2 Bytes unter den Tisch fallen. In einem gewissen Sinnde ist das für einen µC das, was für dich eine Division durch 10 ist: Besonders einfach, denn um durch 10 zu dividieren brauchst du nicht rechnen. 4789 / 10 rechnest du ganz einfach, in dem du die Einerstelle wegfallen lässt und das Ergebnis ist 478. Ähnlich auf deinem AVR: Um durch 256 zu dividieren lässt er einfach das Low-Byte einer int-Zahl wegfallen. Damit hast du ein Ziel: Du möchtest 1.2 gerne als Bruch ausdrücken, weil du ja x * 1.2 durch etwas der Form x * a / b ersetzen willst. Nur hast du gerade eine Zusatzbedingung formuliert. b sollte zumindest eine 2-er Potenz sein, wenn geht dann gleich 256. Also lautet die Frage: welcher Bruch a/b ist eine gute Näherung für 1.2, wobei b gleich 256 sein soll. Ein bischen Umformen bringt uns zu 1.2*b = a, ausgerechnet 1.2 * 256 = 307.2 Da du ganzzahlige rechnen willst, willst du logischerweise nicht x * 307.2 / 256 rechnen, sondern x*307/256 Entgegen kommt dir jetzt, dass du nur ein ganzzahliges Ergebnis brauchst. D.h. von dem 'Fehler', den du durch die Verwendung von 307 anstatt 307.2 einbringst, wird schon mal einiges dadurch sich verlieren, dass dich sowieso nur das ganzzahlige Ergebnis interessiert. UNd für den Rest kann man eine ANalyse machen oder aber, man probiert (zb mit Excel) das ganze einfach mal am PC mit den relevanten Zahlen im relevanten Zahlenbereich durch, ob dort überall das richtige rauskommt. Oder ob man zumindest mit dem Fehler leben kann. Ja auch das gibt es: man rechnet eigentlich ein bischen falsch aus, aber das macht nichts.
:
Bearbeitet durch User
einen Artikel? Ich denke nicht. Ist aber ganz einfach: gehst du aus von int x = ?; float y = ?f; dann ist x * y = x y 256/256 = x*(y*256)/256 wenn du y im Voraus kennst, kannst du (y*256) vorberechnen und als integerzahl im code ablegen. Dann ist x*(y*256) nur noch eine Integermultiplikation für en Controller und aus /256 macht ein guter Compiler >>8 (Zahl um 8 bits nach rechts schieben).
Peter II schrieb: > oder man teilt es einfach auf > > x = 1000 > x += (1000/10) * 2 Wozu soll diese Aufteilung gut sein?
Rolf Magnus schrieb: > Peter II schrieb: >> oder man teilt es einfach auf >> >> x = 1000 >> x += (1000/10) * 2 > > Wozu soll diese Aufteilung gut sein? man kann es auch als 1zeiler schreiben 1000 *1,2 = 1000 + ( 1000/10 ) * 2;
Vielen Dank Karl Heinz! Wie immer erstklassig erklärt! Ich werde das mal so implementieren und sehen ob es für meine Anwendung funktioniert. Vielen Dank!
Peter II schrieb: > Rolf Magnus schrieb: >> Peter II schrieb: >>> oder man teilt es einfach auf >>> >>> x = 1000 >>> x += (1000/10) * 2 >> >> Wozu soll diese Aufteilung gut sein? > > man kann es auch als 1zeiler schreiben > > 1000 *1,2 = 1000 + ( 1000/10 ) * 2; Aber welchen besonderen Vorteil hat 1000 + ( 1000/10 ) * 2 gegenüber ( 1000/10 ) * 12?
Rolf Magnus schrieb: > Aber welchen besonderen Vorteil hat 1000 + ( 1000/10 ) * 2 gegenüber > ( 1000/10 ) * 12? das es bei andern zahlen nicht zu "verlusten" kommt. bei 999 macht es einen unterschied, weil 999/10 nur 99 ist.
Wenn die Kommazahl eine Konstante ist, kann man versuchen, die spezifischen Eigenschaften dieser Zahl für ein optimiertes Rechenverfahren zu nutzen. Hier ist ein Beispiel für den Faktor 1,2, das mit ganz einfachen 16-Bit-Operationen auskommt und damit für 8/16-Bit-Controller ohne Hardwaremultiplizierer sehr gut geeignet ist:
1 | uint16_t mul1_2(uint16_t x) { |
2 | uint16_t y, z; |
3 | |
4 | y = x >> 2; |
5 | do { |
6 | z = y; |
7 | y = (x + z) >> 1; |
8 | y = (y + z) >> 2; |
9 | } while(y != z); |
10 | return x + y; |
11 | }
|
Das Argument x darf dabei im Bereich 0..52428 liegen. Der Algorithmus liefert in 1 bis 10 Iterationen (abhängig von der Größe von x) den ganzzahligen Anteil von x·1,2.
:
Bearbeitet durch Moderator
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.