16Bit auf 100 normieren?

Gast #2382618
Lesenswert?

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?
Gast #2382640
Lesenswert?

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
Gast #2382660
Lesenswert?

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.
Gast #2382679
Lesenswert?

> 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.
Gast #2382686
Lesenswert?

> 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.
Gast #2382697
Lesenswert?

> 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.
Gast #2382710
Lesenswert?

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;
Gast #2382737
Lesenswert?

> /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.
Gast #2382753
Lesenswert?

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.
Gast #2382839
Lesenswert?

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.
#2383724
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren