Forum: PC-Programmierung C: Berechnung fehlerhaft?


von Dirk F (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,
bin seit Stunden bei einer Fehlersuche und komme leider nicht weiter.
Als Anlage 2 Zeilen Code in C.
Habe die varriablen zum Testen durch Konstanten ersetzt.

Frage: Warum ist hier e immer positiv ??

Gruß

: Verschoben durch Admin
von MatthiasS_1970 (Gast)


Lesenswert?

wie ist e definiert?

von Helmut S. (helmuts)


Lesenswert?

Weil 32767*2000000 schon überläuft. Das Ergebnis benötigt 38bit. "Long" 
hat aber nur 32bit. wenn dein C das 64bit "long long" kennt, hast du 
gewonnen.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bitte nächstes mal als Text und nicht als Bild posten.

32767 * 2000000 = 65534000000 -> viel zu groß für long auf allen 
gängigen Plattformen. Das Überlauffverhalten für vorzeichenbehaftete 
Werte ist nicht definiert. Damit darf also ein beliebiges Ergebnis 
rauskommen.

von Wolfgang (Gast)


Lesenswert?

Dirk F schrieb:
> Habe die varriablen zum Testen durch Konstanten ersetzt.

Wenn man jetzt wüßte, was an der Ganzzahlrechnung wirklich variable 
Größen sind, könnte man auch über sinnvolle alternative Formulierungen 
nachdenken, um explosionsartige Anforderungen an die Zahl der gültigen 
Bits in der Rechnung zu vermeiden.

von Bastler (Gast)


Lesenswert?

(2^15 -1)/(2^16 -1) ist 1/2 mit einer Abweichung von 7ppm oder einer 
Unsicherheit auf dem 16ten Bit. Der DAC ist ja bestimmt fehlerfrei, 
oder?

von Dirk F (Gast)


Lesenswert?

Hallo,
das ging ja schnell mit den Antworten.

Die Platform ist PIC32  mit dem XC32 compiler.
Ich denke, INT ist da 32 Bit und long 64 Bit  ?

Das Problem:
Der 24 Bit wert vom DAC  (Skaliert auf 0...2000000) soll mit 
Sollwertvorgabe vom 16 Bit DAC (Bereich 0....65535) verglichen werden.
Also muss ich ja erst eine der beiden Werte auf den anderen Bereich 
skalieren.

Hier der aktuelle code in Textform:
1
          e =  (long) 900000 - (((long) 32767 * (long) 2000000 )/ (long) 65535 ) ; // ( ( voltage <<16) /2000000)  - 32000; //data  ;     // Regeldifferenz = Istwert - Sollwert
2
                if ((e > 1) && (PI_corr > -650)) PI_corr -- ;             // um 1 Digit vom 16-Bit DAC korregieren
3
                if ((e < -1) && (PI_corr <  650)) PI_corr ++ ;             // wird ausgef max 1 % in 6,5 s ausregeln

--

Und das nächste Mal liest Du auch noch den Text, der über dem 
Eingabefeld steht, und verwendest die [ c ] [ /c ] -Tags, dann kann man 
den Kram .. naja .. besser lesen.

-rufus

: Bearbeitet durch User
von Dirk F (Gast)


Lesenswert?

sorry, meine: ...Der 24 Bit wert vom ADC

von Dirk F (Gast)


Angehängte Dateien:

Lesenswert?

HI, habe den Fehler gefunden.
long ist 32 Bit, long long ist 64 Bit...

Danke an alle !!!! uper forum hier.

von Wolfgang (Gast)


Lesenswert?

Dirk F schrieb:
> Der 24 Bit wert vom DAC  (Skaliert auf 0...2000000)

Blöde Idee.

Die Sache wird aber einfacher, wenn du 65536 auf 2000030 oder 2000031 
skalierst. Und der Genauigkeitsfehler verschwindet im 
Digitalisierungsrauschen vom 16-Bit ADC.

> Hier der aktuelle code in Textform:
Du darfst das gerne mit "pre" taggen.

>
>           e =  (long) 900000 - (((long) 32767 * (long) 2000000 )/ (long)
> 65535 ) ; // ( ( voltage <<16) /2000000)  - 32000; //data  ;     //

Und was davon ist jetzt wirklich konstant?

von (prx) A. K. (prx)


Lesenswert?

Rolf Magnus schrieb:
> 32767 * 2000000 = 65534000000 -> viel zu groß für long auf allen
> gängigen Plattformen.

Ohne den üblichen Streit starten zu wollen möchte ich doch anmerken, 
dass 64-Bit Linux durchaus als gängige Plattform betrachtet werden darf. 
Und da ist "long" 64 Bits breit (LP64 Modell). Dass Microsoft sich das 
nicht getraut hat (LLP64 Modell) liegt wohl an der Historie von deren 
APIs und den darin enthaltenen Typdefinitionen.

: Bearbeitet durch User
von Dirk F (Gast)


Lesenswert?

>>>Digitalisierungsrauschen vom 16-Bit ADC.

Verwende einen 24 Bit ADC . Habe eine rauschfreie Auflösung 0.5 PPM 
erreicht.

>>>Und was davon ist jetzt wirklich konstant?

Hier der code, der funktioniert:
e =  (long) voltage - (((long long) data_roh * (long long ) 2000000 )/ 
(long long ) 65535 ) ;

von Yalu X. (yalu) (Moderator)


Lesenswert?

Eine sehr gute Näherung für 2000000/65535 ist 26215/859 (der relative
Fehler beträgt nur 1.5E-8). Damit kannst du das Ganze auch ohne
64-Bit-Arithmetik rechnen.

Die Division kannst du ebenfalls einsparen, wenn du sowohl den Ausdruck
für e als auch die Fehlergrenzen (±2) jeweils mit 859 multiplizierst.
Dann sieht der Code folgendermaßen aus:
1
  e =  voltage * 859 - data_roh * 26215;
2
  if ((e >=  2 * 859) && (PI_corr > -650)) PI_corr -- ; 
3
  if ((e <= -2 * 859) && (PI_corr <  650)) PI_corr ++ ;

Bei dieser Methode entfällt zudem der Rundungsfehler durch die
ganzzahlige Division.

von Ralf G. (ralg)


Lesenswert?

Da mir solche Divisionen durch 65535 (oder durch 255, ...) überhaupt 
nicht gefallen, verschiebe ich den 'Rundungsfehler' lieber an das untere 
Ende des Messwertes.
z.B.:
Messwert ist 1023 -> Messwert + 1 -> 1024 -> und jetzt, falls es hier 
nötig ist, schon was anzupassen: durch 4, statt 1023 / 4 -> jetzt noch 
minus eins und somit hat der Compiler die Möglichkeit, die Division 
durch Bitschieben zu ersetzen.

In dem vorliegenden Fall könnte man dann mit 15625 / 512 rechnen.
(Falls die Genauigkeit 7.9e-5 reicht, auch mit 7812 / 256)

von DirkF (Gast)


Lesenswert?

>>>Damit kannst du das Ganze auch ohne 64-Bit-Arithmetik rechnen.

Diese Berechnung wird jede 250 ms ausgeführt. Aus Prozessorsicht eine 
Ewigkeit. Ich denke, eine 64-Bit Berechnung wird die Gesamt Performance 
vom System nicht sehr stark beeinträchtigen.

>>>Eine sehr gute Näherung für 2000000/65535 ist 26215/859

Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die 
Wartung des Programms zu verbessern, ist die 
Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode 
besser.

Danke für eure Hilfe!
Gruß

von Wolfgang (Gast)


Lesenswert?

DirkF schrieb:
> Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die
> Wartung des Programms zu verbessern, ist die
> Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode
> besser.

Man kann alles übertreiben.

Irgendwie sollte es doch möglich sein, zwei Programmzeilen auch nach 
einem Jahr noch zu überblicken. Und zur Not gibt es die Möglichkeit, in 
solchen Fällen durch einen Kommentar Licht ins Dunkle zu bringen und so 
den Sinn solcher Formulierung zu erhellen.

von stdint (Gast)


Lesenswert?

Ich würde empfehlen in zukunft die <stdint.h> zu includieren und dann 
mit diesen Datentypen zu arbeiten. Dann gibt's kein "Ich dachte long 
sein 64bit" mehr, einfach int64_t.

von Yalu X. (yalu) (Moderator)


Lesenswert?

DirkF schrieb:
> Diese Berechnung wird jede 250 ms ausgeführt.

>>>>Eine sehr gute Näherung für 2000000/65535 ist 26215/859
>
> Danke für den guten Hinweis. Aber ein Argument spricht dagegen: Um die
> Wartung des Programms zu verbessern, ist die
> Lesbarkeit/Nachvollziehbarkeit nach z.B. 1 Jahr mit der ersten Methode
> besser.

Ok, da stimme ich dir zu. Wenn weder Rechenzeit noch der Programm-
speicherverbrauch kritisch sind, ist es besser, die übersichtlichere
Lösung zu wählen.

Wolfgang schrieb:
> Man kann alles übertreiben.
>
> Irgendwie sollte es doch möglich sein, zwei Programmzeilen auch nach
> einem Jahr noch zu überblicken.

Den obigen Näherungswert habe ich über die Kettenbruchentwicklung von
2000000/65535 bestimmt. Wird nun aus irgendeinem Grund die Skalierung
der Werte geändert, so dass der Wertebereich von voltage nicht mehr
nur bis 2000000, sondern bspw. bis 3000000 reicht, müsste die Näherung
neu berechnet werden. Im anderen Fall genügt es, im Programm einfach den
Wert 2000000 durch 3000000 zu ersetzen.

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.