GCC Ich hab ebis heute keine Typendefinition für GCC gefunden.. ( Wertebereiche für float usw.) deashalb ist mir foglendes passiert. ! In einem Interrupt, den ich als Tickcounter benutze, zähle ich 0.016384 auf eine floatvaribale gfMSTicksT2 += 0.016xxx Ab 262144.0 + 0.016 kommen falsche Ergebnisse raus. ( 262144.031250 ) ( also schon der doppelte Wert ! ) Ab 524287.0 + 0.016) immer noch ( 524287.031250 ) = FALSCH !!! Ab 524288.0 + 0.016 aber wird der Addierwert zu NULL!!! gerundet 524288.0 += 0.016 ( 524288.000000 ) ist FALSCH !! Vorsicht also, bei eiem Zeitzählerverwenung mit Float... Deshalb ist der ATMEGA16 nach 4 1/2 Tagen = >524288 Sekunden ..stehen geblieben, weil kein Zeitvergleich in der Software mehr war... gfMSTicksT2 + 0.16 wird nicht mehr inkrementiert. MaAcht einfach diesen Feler nihct mehr... Das dies so früh passiert, damit hätte ich nie gerechnet... Ciao Christof gfMSTicksT2 = 0x7FFEF + 0.016// AB hier wird PrintFloatCRLF( 262143.0 + 0.016); //262143.015625 ==0x3FFFF= 262143 PrintFloatCRLF( 262144.0 + 0.016); //262144.031250 PrintFloatCRLF( 524287.0 + 0.016); //524287.031250 ==0x7FFFF PrintFloatCRLF( 524288.0 + 0.016); //524288.000000
Für AVR-GCC ist sizeof (float) = 4, es werden also nur recht ungenaue 32-Bit-Floats verwendet. In float.h sollten FLT_MAX und FLT_MIN zu finden sein. Hier mal -als Beispiel- die entsprehenden Konstanten, allerdings aus einem anderen Compiler:
1 | #define FLT_DIG 6 /* # of decimal digits |
2 | of precision */
|
3 | #define FLT_EPSILON 1.192092896e-07F /* smallest such that |
4 | 1.0+FLT_EPSILON != 1.0 */
|
5 | #define FLT_GUARD 0
|
6 | #define FLT_MANT_DIG 24 /* # of bits in |
7 | mantissa */
|
8 | #define FLT_MAX 3.402823466e+38F /* max value */ |
9 | #define FLT_MAX_10_EXP 38 /* max decimal exponent |
10 | */
|
11 | #define FLT_MAX_EXP 128 /* max binary exponent |
12 | */
|
13 | #define FLT_MIN 1.175494351e-38F /* min positive value |
14 | */
|
15 | #define FLT_MIN_10_EXP (-37) /* min decimal exponent |
16 | */
|
17 | #define FLT_MIN_EXP (-125) /* min binary exponent |
18 | */
|
64-Bit-Floats hat wohl noch niemand auf diesen 8-Bit-Controller portiert. Könnte es sein, daß es keine besonders gute Idee ist, a) floating-Point-Arithmetik in einer Interrupt-Routine zu betreiben und b) eine float-Variable als Tickercounter zu verwenden?
Nachtrag: Sorry für die vergurkte Formatierung. Leider gibt's hier keine Vorschau und keine Edit-Funktion ...
> Vorsicht also, bei eiem Zeitzählerverwenung mit Float... > Deshalb ist der ATMEGA16 nach 4 1/2 Tagen = >524288 > Sekunden ..stehen > geblieben, weil kein Zeitvergleich in der Software mehr war... > gfMSTicksT2 + 0.16 wird nicht mehr inkrementiert. > > > MaAcht einfach diesen Feler nihct mehr... Es gibt auch double, wenn man mehr Präzision braucht. > Das dies so früh passiert, damit hätte ich nie gerechnet... Dann hast du vermutlich noch nicht viel mit Fließkommaberechnungen zu tun gehabt. In C muß die Genauigkeit nicht höher als 6 signifikanten Dezimalstellen sein, und viel mehr wird es bei den üblichen 32 bit für float auch nicht. Da du schon 6 Stellen vor dem Komma hast, brauchst du dich nicht zu wundern, daß nach dem Komma nichts sinnvolles mehr rauskommt. Aber warum benutzt du überhaupt Fließkomma? Mir scheinen Fixkomma-Rechnungen hier sinnvoller zu sein.
double = float beim avr-gcc, wie bereits oben bemerkt wurde. Eine Anwendung die 64 Bit doubles braucht ist auf einem 8 Bitter soundso normal verloren, es sei denn Zeit spielt keine Rolle :) Wenn man sich jedoch überlegt, dass selbst anspruchvolle DSP-Anwendungen mit 16-32 Bit Festkomma auskommen, liegt der Fehler wohl meist eher beim Anwender.
Ich würde hier auch nicht jedesmal eine Fließkommazahl um irgendeinen komischen Wert erhöhen, sondern stattdessen einfahch einen Integer inkrementieren. Den rechne ich dann nachher da, wo ich das brauche, in den Zielwert um (möglichst Fixkomma oder Integer). Das dürfte deutlich schneller und kompakter sein, und die Genauigkeit bleibt nicht ab irgendeinem Wert auf einmal auf der Stecke. Den Integer kann ich durch Kaskadierung auch "beliebig" groß machen, je nachdem, wie weit gezählt werden muß. Fließkomma ist eine heikle Sache, vor allem, wenn man inkrementell rechnen muß. Fehler pflanzen sich fort, die Genauigeit reicht vorne und hinten nicht, es wird gerundet, wo man's nicht will. Man kann keine exakten Vergleiche machen, ... Und beim Mikrocontroller kommt noch dazu, daß es einen gewaltigen Overhead hat.
Mhhh, die 0.016384 sehen irgendwie nach 1/61 aus. Wie wäre es damit: uint32_t gfMSTicksT2; uint8_t tmpticks; if (++tmpticks==61) { gfMSTicksT2++; tmpticks=0; } War es nicht sogar so, daß manche unverdächtige Dezimalzahl (1/10) sich als unendliche Gleitkommazahl entpuppt? Falk
> Mhhh, die 0.016384 sehen irgendwie nach 1/61 aus. Für mich sieht's eher aus wie 2 hoch 14 geteilt durch eine Million. Das wäre das gleiche wie 256/15625. > War es nicht sogar so, daß manche unverdächtige Dezimalzahl (1/10) > sich als unendliche Gleitkommazahl entpuppt? Nicht als unendliche, aber als periodische. Genauso wie 1/3 im Dezimalsystem nicht darstellbar ist, ist 1/5 (und damit auch 1/10) im Dualsystem nicht darstellbar.
@Rolf Magnus: So oder so: Wir meinten etwa das Gleiche. Mein Lieblingsbeispiel für die "Macht des Integers": uint32_t r,n; r=n*(125.000.000 * 4.294.967.296); in 80 Zeilen AVR_Assembler. Falk
> Ab 524288.0 + 0.016 aber wird der Addierwert zu NULL!!! gerundet > 524288.0 += 0.016 ( 524288.000000 ) ist FALSCH !! Ja ja, sowas kommt dabei raus, wenn Mathematiker in C programmieren wollen. In C gibt es keine unendlich genauen Zahlen, daher ist das Ergebnis unter C RICHTIG !! Wie ja schon gesagt wurde, nimm Ganzzahlen, Float in Interrupts nehmen nur Ignoranten (bloß nicht zuviel Rechenzeit fürs Main übriglassen). Ganzzahlen lassen sich auch prima kaskadieren: unsigned long time0, time1, time2; if( ++time0 == 0 ) if( ++time1 == 0 ) ++time2; Und schon hast Du einen 96 Bit Zeitstempel. Ist zwar immer noch nicht unendlich genau, sollte in der Praxis aber reichen. Da rauft sich dann zwar der Mathematiker die Haare (positive Zahl kann nach Increment nie Null werden), in C geht das aber. Peter
Ihr seit ja richtig gut und nett. ich dachte einfach den 8 Bit Timer als Tickcounter zu missbrauchen wäre eine gute Idee, und so genaue Sekunden und schnelle mS Zeitabschnitte zu bekommen... um eben genaue Sekunden zu bekommen müßte ich ja 0.016384 ms mit dem Faktor 1 000 000 multpliziren... und dann //unsigned long ulMSTicks; ulMSTicks += 16384; und auf ein ULONG aufaddieren....!!! Phuuu, dass klingt aber auch nicht so toll. !! Und dann muss ich auch den Zähler VOR den Überlauf zurückstellen, damit kein Zeitvergleich ( ZEitdistanz) auf einmal NEGATIV werden kann.....Wichtig z.B. bei Entprellroutinen für Tastaturen etc.. Aber allen Vielen Dank für eure Gedanken //Bis jetzt war es so implementiert !!!! #define TIMER2_INTPERIODE 0.016384 //mS ad volatile SIGNAL(SIG_OVERFLOW2) // ******************************************************************* { sei(); //INTERRUPT ENABLE // 16MhZ / 1024 1024 256 = 61,03515625 Hz // T= 1/f = 0.016384 ms / Interrupt gfMSTicksT2 += TIMER2_INTPERIODE; // JETZT STELLE ICH EINFACH DEN ZÄHLER ZURÜCK //Um einen floaTrundungsfehler zu verhindern ! if(gfMSTicksT2 > HALF_FLOAT_ROUNDERRORPOINT ) // --->> 0x3FFFF { ResetCountVals(); //Wegen Ausfallgefahr }; };
"Und dann muss ich auch den Zähler VOR den Überlauf zurückstellen, damit kein Zeitvergleich ( ZEitdistanz) auf einmal NEGATIV werden kann" Nein !!! Mußt Du eben nicht, wenn Du Ganzzahlen nimmst. Es ist egal, welche Zahl kleiner oder größer ist, die Differenz ist immer richtig, wenn Dein Zeitinterval nie länger ist als der Zählbereich der Zeitstempelvariable. Und das ist auch genau der Grund, warum alle Welt Ganzzahlen für die Timerticks nimmt. Selbst auf dem PC ist das so, da gibt es ja diese ominöse Konstante 18,2 mit der man dann die genauen Sekunden rauskriegt. Peter
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.