Unsigned Integer Wert auf 100er Runden

OP #1098981
Lesenswert?

Hallo!

Vielleicht kann mir jemand von Euch helfen. Ich möchte einige 
Spannungswerte in mV über die UART ausgeben.
Die Werte (Datentyp unsigned int) liegen zwischen 0mV und 35000mV. Damit 
die Werte nicht zu sehr schwanken würde ich gern in 100mV Schritten 
runden.

Bsp.:

Gemessener Wert:             Ausgegebener Wert:
24113mV                      24100mV
24159mV                      24200mV

Bin mittlerweile schon ziemlich am verzweifeln.
Habt Ihr vielleicht eine Idee???

Gruß
Daniel
#1098984
Lesenswert?

Woran verzweifelst Du konkret? Anhand der Erwähnung von unsigned int 
gehe ich mal davon aus, dass es sich um C handelt, wobei eine explizite 
Erwähnung der Programmiersprache eigentlich zu den 
Selbstverständlichkeiten gehört. Und eine Rundung ist in C eigentlich 
kein Problem, allerdings müsstest Du dazu schon mal ein paar 
Randbedingungen rausrücken...
OP #1099004
Lesenswert?

Ja genau es handelt sich un C, hatte ich vergessen zu erwähnen.

Das Problem ist, dass ich keinen float-Wert auf "x" Kommastellen runden 
möchte, sondern wie in dem Beispiel angegeben einen unsigned int in 
100er Schritten.

Der Wert der sich also in der Variablen befindet beträgt wie im Beispiel 
24113. Nun würde ich diesen Wert gerne so runden, dass anschließen 24100 
in der Variablen steht.

Fehlt sonst noch etwas?
OP #1099009
Lesenswert?

Karl Zeilhofer wrote:
> gerundet = ((wert + 50)/100)*100;
>
> fertig!
>
> Edit: wenn die Werte nicht so sehr schwanken sollen, dann wäre
> vielleicht auch ein Filter angebracht. Denn ist der Messwert um 24100
> herum, so schwankt es wieder hin und her.

Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit 
für eine weitere Multiplikation und Division habe.
#1099011
Lesenswert?

Karl Zeilhofer wrote:
> Edit: wenn die Werte nicht so sehr schwanken sollen, dann wäre
> vielleicht auch ein Filter angebracht. Denn ist der Messwert um 24100
> herum, so schwankt es wieder hin und her.
Richtig, eine Rundung glättet nur unzureichend. Glätten kann man mit 
einer (vernünftigen) Mittelwertbildung.
#1099016
Lesenswert?

Da Kn wrote:
> Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit
> für eine weitere Multiplikation und Division habe.
Ach, guck mal da. Genau das sind die Randbedingungen, die Du oben 
verschwiegen hast. Dazu gehört neben der Programmiersprache auch, was 
für Ressourcen Dir zur Verfügung stehen. So langsam kommt es also 
raus...

Der nächste Punkt (nachdem Du Deine Hardware mal beschrieben hast) wäre 
jetzt die Frage, was mit den gerundeten Werten überhaupt geschehen soll. 
Eine Multiplikation (Division) mit (durch) eine(r) Konstante benötigt 
auf den meisten Systemen übrigens gar nicht so viel Zeit. Wenn es nicht 
grad um Mikrosekunden geht, ist das relativ unkritisch. Aber wie schon 
gesagt: Das hängt von den verdammten Randbedingungen ab, die bislang nur 
Du selbst kennst!
OP #1099021
Lesenswert?

Also bei dem Controller handelt es sich um einen MSP430F2272, der mit 
einer Taktfrequenz von 16Mhz läuft.

Es sollen 3 Werte in 100er Schritten gerundet werden. Ich habe jetzt 
gerade nocheinma nachgemessen. Für die Rundung eines Wertes über das 
Verfahren was Karl vorgeschlagen hatte benötige ich für einen Wert eine 
Zeit von ca 30us.

Ich könnte mich mit einer Zeit von maximal 10us anfreunden.
#1099040
Lesenswert?

Tim T. wrote:

> Randbedingung: Nur Positive Messwerte.

Weitere Randbedingung: funktioniert nur, wenn der Messwert kleiner 100 
ist. Man kann mit dieser Methode also Messwerte kleiner 100 zu 0 oder 
100 runden.
Man bräuchte schon noch zusätzlich ein Modulo, und das braucht dann 
wieder signifikant Zeit.
OP #1099042
Lesenswert?

> Addiere 50 auf deinen Messwert.
> Wenn Messwert größergleich 100 ist,
>  setze die beiden letzten stellen 0.
> Sonst
>  setze Messwert 0.
>
>
> Randbedingung: Nur Positive Messwerte.


Soweit logisch:
1
Wert = Wert + 50;
2
if(Wert >= 100)
3
   //setze letzten 2 stellen auf 0;
4
else
5
   Wert = 0;

Aber wie setze ich in C die letzten zwei Stellen auf 0?
#1099052
Lesenswert?

Da Kn wrote:

> Soweit logisch:

Nö, denn die Methode rundet bei den meisten Werten (alle größer 100) 
nicht, sondern streicht nur die letzten beiden Stellen.

> Aber wie setze ich in C die letzten zwei Stellen auf 0?

Indem du den Modulo 100 abziehst.

Für ein korrektes Funktionieren dieser Methode bräuchte man also zweimal 
den Modulo-Operator. Dürfte (wenn überhaupt) nur unwesentlich schneller 
sein, wie einmal * und einmal /.
#1099079
Lesenswert?

Karl Zeilhofer wrote:
> Da Kn wrote:
>> Stimmt das klappt. Das Problem ist, dass ich wahrscheinlich keine Zeit
>> für eine weitere Multiplikation und Division habe.
>
> Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht
> zwar nicht so schön aus, erfüllt aber den Zweck...
Gast #1099086
Lesenswert?

1.) In zeitkritischen Anwendungen würde ich auf'm MSP430 nie 
Multiplikationen oder gar Divisionen berechnen lassen. Wer µC 
programmiert, sollte sich mit dem Binärsystem anfreunden und z.B. 
Multiplikationen mit 2, 4, 8, 16, usw. per Shiftoperation durchführen 
(Division analog).

Bei 16MHz hast Du in 10µs genau 160 Taktzyklen Zeit die Berechnung 
durchzuführen. Das ist nicht wenig aber für Mul und Div zu wenig!
Wie wär's, wenn Du nicht auf 100'er sondern z.B auf 128'er Runden 
würdest?
1
  value = ((value + 64)>>7);
2
  value = value<<7;
Das dürfte bei 16MHz in etwas mehr als 1µs erledigt sein!

Allerdings wird Dein Compiler da noch was Optimieren wollen,
was man aber auch "von Hand" hinbekommt, wenn man nämlich das Runden 
durch Ausmaskieren den unteren Bits bewerkstelligt:
1
  value = (value + 64) & 0xFF80;
Das wären dann 4TZ @ 16MHz = 250ns... schneller geht's kaum!

2.) Wie schon erwähnt wurde, bekämpft man "zappelnde" Werte nicht mit 
Runden, sondern mit Mittelwertbildung!
Auch hier ist das Binärsystem Dein Freund, wenn Du 2, 4, 8, usw. Werte 
zum Mitteln nimmst, weil dann die Division per Shift wieder einfach und 
schnell wird!
#1099094
Lesenswert?

Stefan Ernst wrote:
> Karl Zeilhofer wrote:
>> Dann runde halt nich im Dezimalen, sondern im Binären mit Shift. Sieht
>> zwar nicht so schön aus, erfüllt aber den Zweck...
>
> Dann erkläre doch mal, wie man das macht, im Binären mit Shift auf *100*
> zu runden?

Stefan (Gast) hats bereit genau nach meiner Vorstellung vorgeführt.
Gast #1099097
Lesenswert?

>Und wo bitte wird da auf 100 gerundet?
>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte
>hat.
Das war ja auch ein Vorschlag...!
Wenn ich für eine Rundung 30µs brauche aber nur 10µs zur Verfügung habe, 
muss man halt Kompromisse eingehen! ;-)
Gast #1099106
Lesenswert?

>Und wo bitte wird da auf 100 gerundet?
>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte
>hat.
nur weil es im Decimalsystem nicht schön aussieht ist es doch im 
Binärsystem gut gerunden. Man muss halt etwas universeller Denken.

(Es gibt genau 10 Arten von Menschen. Die, die binär verstehen, und die, 
die es nicht verstehen.)
#1099118
Lesenswert?

Peter wrote:
>>Und wo bitte wird da auf 100 gerundet?
>>Da wird auf 128 gerundet, so dass er als Ergebnis wieder "krumme" Werte
>>hat.
> nur weil es im Decimalsystem nicht schön aussieht ist es doch im
> Binärsystem gut gerunden. Man muss halt etwas universeller Denken.

Und welchen Zweck erfüllt dann der im Binärsystem gerundete Wert? Gar 
keinen, denn eine korrekte Wertberuhigung (wie schon mehrfach erwähnt) 
macht man per Mittelwertbildung. Der einzige Grund für eine Rundung sind 
halt "schönere" Werte.
Gast #1099781
Lesenswert?

Jein, in der Sache ansich richtig, nur mein Ton gefiel mir selber 
nicht^^

Abgesehen davon war ich in der Annahme, da ich nicht genau gelesen habe, 
das der OP die Messwerte als ASCII überträgt, da ist ein Nullen 
natürlich einfach...

Da dem aber wohl nicht so ist, fiele mir auch nichts ein ausser:
1
Messwert += 50;
2
if (Messwert<100) Messwert=0;
3
else Messwert-=Messwert%100;
4
sende(Messwert);
ein, wobei ich aber denke das der Modulo länger als eine Multiplikation 
und Division zusammen braucht.


Ein anderer Ansatz wäre den Messwert erst zu Senden wenn er mehr als 
100mV vom alten verschieden wäre:
1
if (Messwert>MesswertAlt+100){
2
 MesswertAlt=Messwert;
3
 Sende(Messwert);
4
};
5
elseif (Messwert+100<MesswertAlt){
6
 MesswertAlt=Messwert;
7
 Sende(Messwert);
8
};
Rundet zwar nicht direkt, sorgt aber dafür das das Zappeln aufhört.

Alles in allem aber auch nicht ganz meine Lösungsart, da ich in diesem 
Falle eher die Auflösung des ADC runterschrauben würde...
#1099795
Lesenswert?

Wie wärs damit:
1
unsigned Messwert();    // Gibt den nächsten Meßwert zurück
2

3
unsigned P10[] = { 10000, 1000, 100 };
4
long V = (long) Messwert();
5
unsigned R = 0;
6

7
for (int i = 0; i < arraysize(P10); i++) {
8
   do {
9
      V -= P10[i];
10
      R += P10[i];
11
   } while (V >= 0);
12
   V += P10[i];
13
   R -= P10[i];
14
}
15

16
if (V >= 50)
17
   R += 100;
18

19
// In R steht jetzt der auf 100 gerundete Wert

Obs letztlich kleiner/schneller ist, als Code mit Division weiß ich 
nicht... Das Prinzip läßt sich noch verfeinern.
#1099847
Lesenswert?

Version 2:
1
unsigned Messwert();    // Gibt den nächsten Meßwert zurück
2

3
unsigned P[] = { 51200, 25600, 12800, 6400, 3200, 1600, 800, 400, 200, 100 };
4
long V = (long) Messwert();
5
unsigned R = 0;
6

7
for (int i = 0; i < arraysize(P); i++) {
8
   if ((V -= P[i]) < 0)
9
      V += P[i];
10
   else
11
      R += P[i];
12
}
13

14
if (V >= 50)
15
   R += 100;
16

17
// In R steht jetzt der auf 100 gerundete Wert
#1099856
Lesenswert?

Da Kn wrote:
> Damit
> die Werte nicht zu sehr schwanken würde ich gern in 100mV Schritten
> runden.
>
> Bsp.:
>
> Gemessener Wert:             Ausgegebener Wert:
> 24113mV                      24100mV
> 24159mV                      24200mV

Was meinst du mit "schwanken" deine ausgegebenen Werte haben ein 
größeres Delta als deine gemessenen Werte.

Was du willst ist quantisieren. Also Genauigkeit für die Ausgabe 
verschwenden.

Wenn du das Schwanken eindämmen willst, nimm die schon erwähnte Methode 
des Mittelwertes, dies geht sehr effektiv mit:
1
y = (x_alt + x_neu)>>1; /* (alt+neu)/2 */

Das mit deinen 100 ist natürlich ne ganz spezielle Anforderung. Wie auch 
schon erwähnt wurde kann man sowas mit 2^n Werten am effektivsten 
umsetzen.

ansonsten kommt in etwa sowas raus:
1
unsignend int a = 30154;
2
unsignend char rest;
3

4
rest = a % 100; /* an modulo in irgendeiner form kommt man nicht vorbei */
5

6
a = a  - rest;
7

8
if (rest >= 50){
9
  a = a + 100;
10
}else{
11
  /* nix */
12
}


GRUß ;-)
Gast #1099873
Lesenswert?

Hey Daniel,

seit wann verwendet ihr denn an der FH MSP? ;-)


Achja, wenn du die Werte in ASCII über die UART schickt, dann habt ihr 
dort ja auch schon Module und Divison drin, vielleicht kann man dort 
optimieren?

Gruß
ein Ex-Mitstudent

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