Hallo zusammen. Ich habe einen 16Bit Nachkommawert von einer 16Bit Division. Ich möchte den Nachkommawert gern auf 100 oder 1000 normieren. Also für 0 will ich 0 erhalten. Und für 65535 möchte ich 100 oder 1000 bzw. noch besser 99 oder 999 erhalten. Wie bekommt man das elegant hin?
Schlechter Plan. Für Rechnung im µC sind 2er-Potenzen immer praktischer zu rechnen.
1 | uint16_t fNormierung ( uint16_t DivRest ) |
2 | {
|
3 | uint32_t u32Temp = DivRest; |
4 | //--------------------------
|
5 | u32Temp *= 1000; |
6 | u32Temp += 32768; |
7 | //--------------------------
|
8 | return (uint16_t) (u32Temp>>16); |
9 | }
|
Das macht dir aus 0..65535 ein 0..999
Wenn es extrem schnell gehen soll, auf 0-255 normieren (d.h. nur die oberen 8 Bit nehmen) und über eine Tabelle mit 256 Bytes im Flash auf 0-99 um-"rechnen". Ansonsten die Rechenoperation Obere_8_Bit / 255 * 99 so umformen, dass aus dem Multiplikator 99 / 255 etwas wird, bei dem der Nenner eine 2er-Potenz ist, damit die Division zum Bitshift wird - z.B. Ergebnis =(Obere_8_Bit * 99L + 128) >> 8. Der Term "+ 128" dient der Rundung (ich gehe davon aus, dass alles unsigned ist, deshalb +) und >>8 entspricht einer Division durch 256, was nahe genug an 255 ist. Die Multioplikation ist dann der einzige rechenintensive Teil - und ATmegas haben eine schnelle Hardware-Multlikation. Zwar nur 8bit, aber die verwendet der Compiler auch für 8bit x 16bit.
> /255 * 99
nein.
Wenn dann /256 * 100 + halbes Bit.
Genau so, wie es Matthias gezeigt hat wird's richtig !
> Wenn dann /256 * 100 + halbes Bit
Quark. Mit (x * 100 + 128) / 256 wird aus 255 nicht wie gewünscht 99
sondern 100. Also nähert man sich dem richtigen Faktor mit (x * 99 +
128) / 256 und nicht mit (x * 100 + 128) / 256. Mathematisch ist das ein
Fehler beim Multiplikator von 1,00xx vs. 0.99xx.
das halbe Bit macht draus ein kaufmännisches Runden. Trotzdem. Auch ohne dieses ist: /256 * 100 richtig und nicht *99
> Auch ohne dieses ist: /256 * 100 richtig und nicht *99
Jetzt wird es aber albern. Ob man sich dem mathematisch korrekten Faktor
von oben unten nähert, ist mathematisch egal. Es ist idiotisch zu
behaupten, dass eine Lösung "falsch" ist. Wenn man sich dem Faktor von
oben nähert, muss man allerdings damit rechnen, dass das Ergebnis den
vorgesehenen Wertebereich verlässt.
> Auch ohne dieses ist: /256 * 100 richtig und nicht *99
Ich korrigiere mich:
Bei Integer Arithmetik natürlich *100/65536. Zuerst multiplizieren, dann
dividieren. Dadurch verlässt man kurz den 16Bit Wertebereich.
Oder eben höchstes Byte *100 /256, wie Du es gesagt hast. Ist nicht ganz
das gleiche, aber ähnlich.
... ja .. eigentlich genau, wie es Matthias als Code-Beispiel zeigt.
Über das halbe Bit kann man noch diskutieren.
Man kann auf im 16 Bit Wertebereich bleiben, muss dann aber die Rechnung in mehreren kleineren Schritten durchführen, da man sonst Genauigkeit verlieren würde (vor allem bei 0..999): 0 .. 65535 auf 0..99: y = x /8 * 5 / 8 * 5 / 256; 0 .. 65535 auf 0..999: y = x /8 * 5 / 8 * 5 / 8 * 5 / 16;
> /256 kann man sich auch schenken, einfach nur das Highbyte verwenden
Ich hoffe doch sehr, dass der gcc-Optimizer solche Sachen wie >>8, /256
u.ä. automatisch ideal löst, so dass man es hübsch hinschreiben kann.
Beim Cosmic Compiler weiss ich, dass er aus y= x / 256; ein y = MSB(x); macht Ich hab sogar mal gesehen, dass er aus einem y = x / 128; ein: y = MSB(x << 1); macht, da 7x Schieben zu lange dauert. Wobei es auch ähnlich sein kann, denn das höherwertigste Bit darf ja nicht verloren gehen.
dieter kuhn schrieb: > Also für 0 will ich 0 erhalten. > Und für 65535 möchte ich 100 oder 1000 bzw. noch besser 99 oder 999 > erhalten. Pack das Ganze in den low-Teil eines 32 Bit Registers, nimm mal 100 bzw. 1000 und verwende dann den high-Teil des 32 Bit Registers var DW : DWORD; begin DW:= DerRest; DW:= DW * 100; // oder eben 1000 result:= HiWord(DW); end; W.S.
dieter kuhn schrieb: > Hallo zusammen. > > Ich habe einen 16Bit Nachkommawert von einer 16Bit Division. bei einer 16Bit-Integer-Division? -> also den REST > Ich möchte > den Nachkommawert gern auf 100 oder 1000 normieren. ok > Also für 0 will ich 0 erhalten. > Und für 65535 möchte ich 100 oder 1000 bzw. noch besser 99 oder 999 > erhalten. das ist schon mal Quatsch, denn der Rest kann nie größer sein als der bei der Division verwendete Divisor. Damit ändert sich der auf 100 o. 1000 zu normierende Bereich abhängig vom Divisor. den normierten Wert berechnest du mit: REST * 1000 / Divisor oder REST * 100 / Divisor Sascha
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.