PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Berechnung falsch



Chypsylon
12.02.2012, 19:25
Hallo
ich möchte auf meinem avr folgende Rechnung durchführen.



uint16_t line_estimate = 0;
uint32_t wa_numerator = 0;
uint16_t wa_denominator = 0;

uint16_t line_values[8]; //Werte zwischen 0...1023

[...] //line values werden eingelesen und befüllt

for(uint8_t i=0;i<8;i++)
{
wa_numerator += line_values[i] * (i+1) *1000;
wa_denominator += line_values[i];
}

line_estimate = wa_numerator/wa_denominator;

//Ausgabe
char buffer[20];
lcd_puts(itoa(line_estimate, buffer, 10));



Allerdings funktioniert das nicht richtig. Wenn z.b alle Elemente von line_values 0 sind und nur das 1 einen anderen Wert hat müsste line_estimate 1000 sein, es schwankt aber nur "wild" hin und her und erreicht zeitweise negative bzw. Werte kleiner 1000 die es niemals erreichen dürfte. Meine Vermutung ist das ich noch irgendwo "typecasten" muss, ich weiss aber nicht wo :confused:
Hat irgendjemand eine Erklärung für dieses seltsame Verhalten bzw. kann mir sagen wo der Fehler liegt?

sternst
12.02.2012, 20:44
Diese Berechnung
line_values[i] * (i+1) *1000 erfolgt in uint16_t. Für line_values[7] z.B. läuft das schon für alles >8 über.

Chypsylon
12.02.2012, 20:53
Diese Berechnung
line_values[i] * (i+1) *1000 erfolgt in uint16_t. Für line_values[7] z.B. läuft das schon für alles >8 über.
Deswegen ist wa_numerator ja auch als 32bit deklariert?




uint32_t wa_numerator = 0;
...
wa_numerator += line_values[i] * (i+1) *1000;

Oder seh ich grad den Wald vor lauter Bäumen nicht:confused:

sternst
12.02.2012, 21:34
Deswegen ist wa_numerator ja auch als 32bit deklariert?Und?
Der Typ auf der linken Seite einer Zuweisung hat keinerlei Einfluss darauf, wie auf der rechten Seite der Ausdruck ausgewertet wird.

Chypsylon
12.02.2012, 21:38
Ich hab auch schon probiert die rechte Seite als long zu casten, hat aber nicht wirklich was geändert...



wa_numerator += (long)(line_values[i]) *((i+1) *1000);
bzw.
wa_numerator += (long)(line_values[i] *((i+1) *1000));

sternst
12.02.2012, 21:43
Was soll das "bzw." denn bedeuten? Die beiden Zeilen sind in ihrem Verhalten grundverschieden. Was genau hast du denn nun ausprobiert?

Chypsylon
12.02.2012, 21:49
Ich hab beide Varianten probiert. Meinem Verständnis nach sollte die erste eigentlich richtig sein:(

sternst
12.02.2012, 21:51
Dann liefere doch mal ein konkretes Beispiel. Was steht in line_values und was kommt als Ergebnis raus?

Chypsylon
12.02.2012, 22:19
line_values[0] ist 600 die anderen sind 0.

Das Ergebnis ändert sich jedoch ständig (Abstand zwischen den Messungen ist ca. 100ms)

Offensichtlich wird das Ergebnis ständig dividiert und der Divisior wird proportional kleiner (siehe angehängte Grafik)

00016
00016
00016
00016
00016
00016
00016
00016
00016
00016
...(16 blieb anfangs ziemlich lange)
02412
01061
00684
00507
00405
00337
00290
00255
00228
00206
00188
00174
00161
00151
00142
00134
00127
00121
00115
00110
00106
00102
00098
00094
00091
00088
00086
00083
00081
00079
00077
00075
00073
00071
00070
00068
00067
00066
00064
00063
00062
00061
00060
00059
00058
00057
00056
00055
00054
00054
00053
00052
00052
00051
00050
00050
00049
00049
00048
00047
00047
00046
00046
00046
00045
00045
00044
00044
00043
00043
00043
00042
00042
00042
00041
00041
00041
00040
00040
00040
00039
00039
00039
00039
00038
00038
00038
00038
00037
00037
00037
00037
00036
00036
00036
00036
00036
00035
00035
00035
00035
00035
00034
00034
00034
00034
00034
00034
00033
06794
02412
01471
01061
00831
00684
00582
00507
00450
00405
00368
00337
00312
00290
00271
00255
00240
00228
00216
00206
00197
00188
00181
00174
00167
00161
00156
00151
00146
00142
00138
00134
00130
00127
00124
00121
00118
00115
00113
00110
00108

sternst
12.02.2012, 23:19
Und mit welcher Code-Variante sind diese Ergebnisse nun entstanden? Die 16 passt jedenfalls genau zu einer "Überlauf-Version".


Das Ergebnis ändert sich jedoch ständig (Abstand zwischen den Messungen ist ca. 100ms)Und hast du mal kontrolliert, ob die Ausgangswerte überhaupt gleich bleiben? Das Ergebnis ändert sich schließlich nicht "einfach so".

Carry
13.02.2012, 01:20
Hallo,

die 16 ist "korrekt": (600 * 1000) / 600 = 600.000 / 600 -> 16 Bit: 10.176 / 600 = 16 :)

Folgende Varianten sollten funktionieren:



wa_numerator += ((uint32_t) line_values[i]) * (i+1) * 1000;

oder

wa_numerator += line_values[i] * (i+1) * 1000UL;


Wichtig ist, dass der Compiler erkennt, dass er auf der rechten Seite in "uint32_t" rechnen soll. Nebenbei, "itoa(...)" passt nicht zu einem "uint", dafür gibt es "utoa(...)".


Carry

Chypsylon
13.02.2012, 16:20
Hat sich erledigt... Man sollte halt nicht vergessen die Variablen nach der Berechnung zu reseten :oops: ](*,)

Diese Variante funktioniert jetzt:


wa_numerator += (long)(line_values[i]) *((i+1) * 100);


DANKE für eure Hilfe :)

EDIT: @Carry: Hab deinen Post leider erst jetzt gesehen, trotzdem danke für die Erklärung :)