Forum: Compiler & IDEs Länge eines Array bestimmen für Mittelwertbildung


von Joachim B. (jojo84)


Lesenswert?

Hallo Forum!

Ich habe mal wieder einen prinzipielle Frage zu souveränem 
Programmieren.

Folgendes hab ich vor: ich lade ein Array mit Messwerten. Der älteste 
Wert wird immer verworfen, und gegen den neuen Messwert ersetzt.
Dann summiere ich alle Array-Elemente auf, und teile dann durch die 
Anzahl der Elemente (normal, Tiefpass-Filter).
Weil ich mehrere verschiedene Dinge messe habe ich eine 
Filterungsfunktion, die das Aufsummieren und Teilen übernimmt. Dieser 
Funktion übergebe ich also u.A. das jeweilige Array mit den letzten x 
Messwerten (und dem neusten).

Jetzt würde ich natürlich gern die Division durch die Anzahl der 
Arrayelemente gegen eine Shift-Operation ersetzen, wenn die Anzahl der 
Array-Elemente ein 2er-Komplement ist. Aber wie stell ich das fest?!

Bisher hab ich das:
1
unsigned char filterung(unsigned char input, unsigned char array[], unsigned char *position)
2
{
3
  unsigned int filterwert = 0;
4
5
  array[*position] = input;
6
  *position++;
7
8
  if(*position > (sizeof(array) - 1))
9
  {
10
    *position = 0;
11
  }
12
13
  for(unsigned char x = 0; x < sizeof(array); x++)
14
  {
15
    filterwert += array[x];
16
  }
17
18
  // und jetzt?!?
19
20
return (unsigned char)filterwert;
21
}

Klar kann ich schreiben:
1
if(sizeof(array) == 2)
2
{
3
filterwert >>= 1;
4
}
5
6
else if(sizeof(array) == 4)
7
{
8
filterwert >>= 2;
9
}
10
.
11
.
12
.

Aber schön ist auch anders :) . Wie komme ich geschickt an diese 
Information?!

Danke schonmal,
Gruß

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Du musst Deiner Funktion die Anzahl der Arrayelemente explizit 
mitteilen, denn auch wenn Du "unsigned char array[]" im 
Funktionsprotoypen verwendest, ist das hier exakt gleichbedeutend mit 
"unsigned char *array" - und aus einem Pointer kann man nicht die Größe 
des zugehörigen Speicherbereichs extrahieren.

von Joachim B. (jojo84)


Lesenswert?

Danke für die Antwort!

Jo, stelle auch grad fest, daß das so nicht geht. Zum Test hab ich mal 
den Simulator angeschmissen und mir die Inhalte der Variablen angeguckt. 
Da sagt er mir, daß sizeof(array) = 2 wäre. Das wundert mich etwas. Ich 
hätte da eigentlich 1 erwartet. Es ist ja ein char-Array...  Kannst du 
mir das vielleicht erklären?

PS: den Fehler mit *pointer++ hab ich selbst schon gefunden :)

von Roland P. (pram)


Lesenswert?

> sizeof(array) = 2

Array ist ein 16bit Pointer nehme ich mal an.


Ob ein Wert eine 2er-Potenz ist, kannst du ggf so feststellen:

i = arrLength;
while (!(i & 0x01)) { i = i >> 1); //  solange schieben, bis ein 
gesetztes Bit kommt
if (i == 1) { ... arrLength ist ein Binärwert }


Gruß
Roland.

von Huch (Gast)


Lesenswert?

>Da sagt er mir, daß sizeof(array) = 2 wäre. Das wundert mich etwas. Ich
>hätte da eigentlich 1 erwartet. Es ist ja ein char-Array...

Nein. Karl Heinz hat ja geschrieben, dass unsigned char *array.
array ist ein Zeiger. Und der Zeiger ist zwei Byte gross.

Namen von Arrays in Ausdrücken sind immer erstmal Zeiger auf Arrays. 
Nicht das Array selbst oder das nullte Element.
Durch die eckigen Klammern wird vor allem ermöglicht solche Arrays ihrer 
Grösse nach zu vereinbaren. Sonst gäbe es nämlich nur noch malloc und 
Konsorten.
Konsequenterweise (oder auch nicht) kann man auch die Index-Schreibweise 
benutzen um Elemente zu adressieren. Aber eigentlich ist das nur ein 
Synonym zur Zeigerarithmetik.

von Huch (Gast)


Lesenswert?

Ich möchte Dir noch einen Vorschlag machen, der die Mittelwertbildung im 
stationären Fall deutlich beschleunigen wird.

Du schreibst:

>Dann summiere ich alle Array-Elemente auf, und teile dann durch die
>Anzahl der Elemente (normal, Tiefpass-Filter).

Eigentlich ist es nicht nötig, jedesmal bei eintreffen eines neuen 
Wertes die Summe aller Array-Elemente zu bilden.
Du kannst, wie Du es schon fast hast, einen Ringpuffer verwenden.
Wenn der Ringbuffer voll ist, dann ist der älteste Wert jeweils der auf 
den neuen Werte folgende, bzw. der Nullte, falls der neue an das Ende 
des Arrays geschrieben wird.
Bei eintreffen eines neuen Wertes, muss nur der älteste Wert von der 
Gesamtsumme abgezogen werden und der neue addiert. Das Array musst Du 
natürlich trotzdem halten um die alten Werte immer wieder subtrahieren 
zu können.

von Michael P. (michael_p)


Lesenswert?

Hallo,

der eleganteste Weg ein Tiefpass-Filter (also PT1-Filter) zu machen ist 
immer nur einen Bruchteil der Differenz aus neuem Eingangswert und 
Ausgangswert, zum Ausgangswert zu addieren.

zB so:
1
unsigned char output=0;
2
3
void filterung(unsigned int input)
4
{
5
   unsigned char diff;
6
7
   diff=output - input;
8
   diff/=10;
9
   output+=diff;
10
}

So brauchst du dir nicht unzählige alte Werte zu merken.
Wenn dich die Codegröße wegen der Division stört, dann dividier halt 
nicht durch 10 sondern schiebe paarmal (also /8 oder /16).

lg,
Michael

von Joachim B. (jojo84)


Lesenswert?

Ok, danke euch allen!

Hab wieder was dazugelernt :) .

Klarheit beseitigt :)

Gruß und gute n8

von Michael B. (mb_)


Lesenswert?

Roland Praml schrieb:
> Ob ein Wert eine 2er-Potenz ist, kannst du ggf so feststellen:
>
> i = arrLength;
> while (!(i & 0x01)) { i = i >> 1); //  solange schieben, bis ein
> gesetztes Bit kommt
> if (i == 1) { ... arrLength ist ein Binärwert }

Das geht wesentlich effizienter:
1
#define is_power_of_two(val) (((val) & ((val) - 1))) == 0)

Das gibt auch true für 0 zurück. Wenn man das nicht will, dann:
1
#define is_nonzero_power_of_two(val) ((val) && (((val) & ((val) - 1))) == 0))

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.