Ich kämpfe mit einem Problem mit WinAvr. Wenn ich in einer Funktion einen double-Wert zu einem long-Wert addiere, geht die Genauigkeit verloren bzw. kommt was falsches raus. konkretes Beispiel: long MjdMidnight; double FracOfDay; int b; double jul = 0.0; if (Month<=2) { Month+=12; --Year;} if ( (10000L*Year+100L*Month+Day) <= 15821004L ) b = -2 + ((Year+4716)/4) - 1179; // Julianischer Kalender else b = (Year/400)-(Year/100)+(Year/4); // Gregorianischer Kalender MjdMidnight = (365L*Year - 679004L + b + (long)(30.6001*(Month+1)) + Day); FracOfDay = Ddd(Hour,Min,Sec) / 24.0; // liefert double jul = (double)MjdMidnight + FracOfDay; return jul; mit Werten: MjdMidnight = 54376 FracOfDay = 0.54027778 jul = 54376.539 Warum ergibt die Addition so ein Zeugs?
Weil die 'double' beim AVR-GCC nur 32 Bit lang sind? und das nur ~6 signifikante Stellen sind? (24Bit Mantisse ink. Vorzeichen, 8Bit Exponent). rtfm, Jörg ps.: Was machst du da eigentlich? und was erwartest du ?
> Was machst du da eigentlich? und was erwartest du ?
Das ist eine Umrechnung in das modifizierte julianische Datum, welches
ich für weitere Berechnungen im astronomischen Bereich brauche. Ich
erwarte eigentlich so was wie 54376.540278. Das dumme ist, in den
Nachkommastellen steckt die Uhrzeit und die ist gerade wichtig für die
weiteren Berechnungen.
Michael Korb wrote: >> Was machst du da eigentlich? und was erwartest du ? > Das ist eine Umrechnung in das modifizierte julianische Datum, welches > ich für weitere Berechnungen im astronomischen Bereich brauche. Ich > erwarte eigentlich so was wie 54376.540278. Die wirs du aber nicht kriegen. Wie Jörg schon sagte: Mit einem double kriegst du mit WinAvr grade mal ca. 6 signifikante Ziffern hin. Blöd bei dir ist halt, dass von diese 6 Stellen bereits 5 auf den Teil vor dem Komma fallen. Damit hast du gerade noch 1 Stelle nach dem Komma, die halbwegs richtig ist, alles andere dahinter ist mehr oder weniger gelogen. > Nachkommastellen steckt die Uhrzeit und die ist gerade wichtig für die > weiteren Berechnungen. Entweder du nimmst einen anderen Compiler, einen bei dem double eine Größe von 8 Bytes hat und nicht 4 wie beim WinAvr.(*) Das würde sich schon alleine deshalb anbieten, weil deine astronomischen Berechnungen auch sonst noch vor Gleitkommaarithmetik strotzen werden und du mit WinAvr-double praktisch keine verlässlichen Ergebnisse kriegen wirst. Die 6 signifikanten Stellen werden nach ein paar Multiplikationen/Divisionen ganz schnell zu 5 und von dort gehts dann noch schneller zu 4, etc. Oder aber du überlegst dir ein Fixpunktschema. Wenn du allerdings alle 6 Nachkommastellen brauchst, wird das schwierig werden. Ein normaler long (32-Bit Ganzzahl) wird da nicht mehr mitkommen. (*) Mit einem 8 Byte double reden wir immerhin von ca. 15 signifikanten Stellen.
Stimmt leider. Hatte ich ganz vergessen, dass der gcc WinAvr ja nur 4Byte double kann. Bin ja schon einmal deswegen auf ARM umgestiegen. Ich werd es mal mit Corssworks AVR versuchen, das ist nicht ganz so teuer. Vielleicht findet sich endlich mal jemand, der eine Lib für 8Byte double baut.
Wäre jetzt nur die Frage offen, ob 64bit Integers für den Zweck nicht ausreichen. Denn die beherrscht er. Das reicht leicht für die gesamte Dauer der menschlichen Existenz in Mikrosekunden gerechnet aus.
Ich verstehe nicht, wozu man für Datum und Uhrzeit float braucht. Gehts mit Ganzzahlen zu schnell oder ist zuviel Flash übrig, den Du voll kriegen mußt ? Beitrag "Berechnung Datum + Uhrzeit + Sommerzeit" Peter
>>Das reicht leicht für die gesamte Dauer der menschlichen Existenz in >>Mikrosekunden gerechnet aus. Nein. 2^64 us sind unter 600000 Jahre. gute Nacht Detlef
Peter Dannegger wrote: > Ich verstehe nicht, wozu man für Datum und Uhrzeit float braucht. > > Gehts mit Ganzzahlen zu schnell oder ist zuviel Flash übrig, den Du voll > kriegen mußt ? Das Problem ist, dass im astronomischen Bereich mit WInkeln in rad gerechnet wird. Das Datum wird dazu in ein modifiziertes julianisches umgerechnet (nach dem Komma ist die Zeit, vor dem Komma Tage) s. hier http://de.wikipedia.org/wiki/Julianisches_Datum, um dann die Sternzeit zu berechnen. Dabei fließen Größen ein, die in rad gegeben sind (geogr. Länge, geogr. Breite). Man müßte versuchen, all diese Lehrbuchfunktionen auf ganzzahlig zu normieren. Ich hab auch schon versucht, die Tage aussen vor zu lassen, weil mich eigentlich nur die Zeit interessiert (nach dem Komma). Allerdings stoße ich dann bei der nächsten Funktion für die Berechnung der Greenwich-Sternzeit wieder auf große double-Zahlen (Konstanten) - das Problem verschiebt sich. Da die Endergebnisse sich immer im rad-bereich abspielen (also 0-2PI), ist dort die Genauigkeit wieder gegeben mit 5-6Stellen nach dem Komma. Weniger bringt Fehler bereits im Minutenbereich und das kann ich mir nicht leisten.
>Das Problem ist, dass im astronomischen Bereich mit Winkeln in rad >gerechnet wird. Das Datum wird dazu in ein modifiziertes julianisches >umgerechnet (nach dem Komma ist die Zeit, vor dem Komma Tage), um dann >die Sternzeit zu berechnen. Hehe Es kommt noch besser, wenn Du bei den tan und atan ankommst, die versalzen Dir so richtig die Suppe. Da kommt eine Grütze raus, dass Du graue Haare bekommst. Die verwegene Idee, es dem Compiler unterzuschieben und einen zu suchen der volle Double berechnet hatte ich auch, war aber eine Sackgasse. Die Ober-Gemeinheit ist, dass Du die Berechnung immer mal wieder für ein paar Tage hingefummelt bekommst und dann am 21.06. 0der 23.12 kommt der grosse Heulkrampf. Mein Tip. Die Formel die Du hast stammt aus Zeiten, als Cäsar Gallien erobert hat. (das ist mal keine Metaffa sondern eine Tatsache) Mach Dich schlau, wie die damals ohne Taschenrechner sowas berechnet haben und bringe diesen Rechnestiel Deinem uC bei, dann geht das auch mit einem handelsüblichen 8Bit uC.
tex wrote: > Es kommt noch besser, wenn Du bei den tan und atan ankommst, die > versalzen Dir so richtig die Suppe. die Kandidaten kommen zum Glück nicht vor. Es beschränkt sich auf reine Grundrechenarten im double-Bereich > Die Formel die Du hast stammt aus Zeiten, als Cäsar Gallien erobert hat. > (das ist mal keine Metaffa sondern eine Tatsache) Mach Dich schlau, wie > die damals ohne Taschenrechner sowas berechnet haben und bringe diesen > Rechnestiel Deinem uC bei, dann geht das auch mit einem handelsüblichen > 8Bit uC. Ich werd mich mal dran setzen. Wahrscheinlich kann ich dann gleich ein neues Buch schreiben - "Astronomische Berechnungen mit dem Taschenrechner" oder so
@ Michael Korb (michaelkorb) >das Problem verschiebt sich. Da die Endergebnisse sich immer im >rad-bereich abspielen (also 0-2PI), ist dort die Genauigkeit wieder >gegeben mit 5-6Stellen nach dem Komma. Weniger bringt Fehler bereits im >Minutenbereich und das kann ich mir nicht leisten. Festkommaarithmetik MFG Falk
@Michael Das Buch brauchst Du nicht zu schreiben, das gibt es schon. Wenn Du mir sagst, was Du berechnen willst, kann ich mal schauen, ob ich Dir da helfen kann.
tex wrote: > Das Buch brauchst Du nicht zu schreiben, das gibt es schon. Due meinst sicher den Mountenbr. bzw. das vo Meus o.ä. > Wenn Du mir sagst, was Du berechnen willst, kann ich mal schauen, ob ich > Dir da helfen kann. Also was ich tun will: - von einem GPS-Empfänger bekomme ich Datum und Zeit (zum Glück wenigstens schon als UT) und geogr. Länge / Breite - daraus will ich die lokale Sternzeit berechnen (erst GMST, dann LST) - später will ich dann noch für Objekte (ausgewählt aus Katalog auf SD-Card) mit gegebener RA und DEC den Stundenwinkel, Azimut und Höhe berechnen. Das ist eigentlich schon alles. Bisher hab ich die Gleichungen aus genannten Büchern in Klassen verpackt und gut. Allerdings unter Compilern, die mit echten double (8Byte) umgehen können. Man müßte nun die wichtigen Funktionen entsprechend so bauen, dass die relevante Anzahl der Stellen nicht überschritten wird oder im long-Bereich rechnen und entsprechend normieren.
>- später will ich dann noch für Objekte (ausgewählt aus Katalog auf >SD-Card) mit gegebener RA und DEC den Stundenwinkel, Azimut und Höhe >berechnen. >Das ist eigentlich schon alles. Das Beste daran ist der letzte Satz. Also bei Azimut unf Höhe kommst Du um den Tan nicht mehr drum herum. Wie schon oben geschriebn, bring dem uC bei, zu fuss zu rechnen, und benutze die Ziffern als Zeichen. Also für eine einfache Addition also zuerst die Position der Kommastellen der beiden Summanden ermitteln, dann Zeichen für Zeichen mit übertrag addieren und Ergebnis Zeichen für Zeichen in einen String schreiben. Wichitg ist auch dass Du Dir mal exemplarisch und zu Fuss ein paar Zahlen anschaust, wie sie sich mit den Jahren ändern. Viele sind über hunderte Jahre hinweg konstant.
tex wrote:
> Also bei Azimut unf Höhe kommst Du um den Tan nicht mehr drum herum.
Ab dem Punkt ist es aber kein Problem ehr, da sich alles im Bereich
0-2PI abspielt. Da reichen die Stellen der Mantisse aus. Das Problem ist
bei großen Zahlen wie dem julianischen modifizierten Datum und der
Berechnung der Greenwich-Sternzeit, die dann bereits in rad vorliegt.
Danach komme ich sicher mit der Anzahl der Stellen aus.
Ich hab die Funktionen für jul. Datum und GMST mal zusammengefasst und
die kritischen Stellen anders gelöst. Das sieht dann so aus:
// Berechnung der mittleren Greenwich-Sternzeit
double calcGMST( int jahr, int monat, int tag, int stunde, int minute,
int sekunde)
{
//
// Konstanten
//
const double Secs = 86400.0; // Anzahl der Sekunden je Tag
//
// Variablen
//
long MjdMidnight = 0L;
double FracOfDay = 0.0;
int b = 0;
double MJD_0 = 0.0, UT = 0.0, T_0 = 0.0;
double T = 0.0, gmst = 0.0, result = 0.0;
/// mjd berechnen
if (monat<=2) { monat+=12; --jahr;}
if ( (10000L*jahr+100L*monat+tag) <= 15821004L )
b = -2 + ((jahr+4716)/4) - 1179; // Julianischer Kalender
else
b = (jahr/400)-(jahr/100)+(jahr/4); // Gregorianischer Kalender
// Anzahl Tage
MjdMidnight = (365L*jahr - 679004L + b +
(long)(30.6001*(monat+1))+tag);
// Zeit als Nachkommawert
FracOfDay = Ddd(stunde, minute, sekunde) / 24.0;
// gmst berechnen
MJD_0 = MjdMidnight; // ganzzahliger Anteil
UT = Secs*(FracOfDay); // [s]
T_0 = (MJD_0-51544.5)/36525.0;
T = ((MjdMidnight + FracOfDay) - 1544.5)/36525.0;
gmst = 24110.54841 + 8640184.812866*T_0 + 1.0027379093*UT
+ (0.093104-6.2e-6*T)*T*T; // [sec]
result = Modulo(gmst,Secs);
result = (M_PI/2.0/Secs)*result; // [Rad]
return result;
}
Das Problem mit dem großen julianischen Datum konnte ich lösen, da es
bei der GMST-Berechnung eh in seinen Bestandteilen verarbeitet wird. Ich
bin nur nicht sicher, ob bei der Zeile gmst= die Konstanten
funktionieren. Das Ergebnis ist bisher noch nicht korrekt.
Hier mal noch die Original GMST-Funktion, in die das jul. Datum
einfließt.
double GMST (double MJD)
{
//
// Konstanten
//
const double Secs = 86400.0; // Anzahl der Sekunden je Tag
//
// Variablen
//
double MJD_0, UT, T_0, T, gmst;
MJD_0 = floor ( MJD );
UT = Secs*(MJD-MJD_0); // [s]
T_0 = (MJD_0-51544.5)/36525.0;
T = (MJD -51544.5)/36525.0;
gmst = 24110.54841 + 8640184.812866*T_0 + 1.0027379093*UT
+ (0.093104-6.2e-6*T)*T*T; // [sec]
return (M_PI/2.0/Secs)*Modulo(gmst,Secs); // [Rad]
}
MJD_0 ist ja eigentlich eh nur der Vorkommateil des jul. Datums (floor)
und UT berechnet sich nur aus dem Nachkommateil des jul. Datums.
Momentan bin ich eh betriebsblind und finde den Fehler nicht. Vielleicht
hat ja jemand eine Idee.
Michael Korb wrote: > bin nur nicht sicher, ob bei der Zeile gmst= die Konstanten > funktionieren. Das wird mit Sicherheit nicht gehen. Der Dynamikumfang von 4-Byte double reicht da bei weitem nicht aus, um ein zuverlässiges Ergebnis zu erhalten. > Momentan bin ich eh betriebsblind und finde den Fehler nicht. Der Fehler liegt darin, dass 4 Byte double einfach zuwenig Reserven haben um Zahlen mit Größenordnungsunterschieden von 10 hoch 13 in einer Berechnung zu verwurschten. > Vielleicht > hat ja jemand eine Idee. Entweder suchst du dir am Netz eine Floating Point Library, die mit 8 Byte double umgehen kann. Oder du suchst nach einer "Multi Precission Library", der du die Stellenzahl vorgeben kannst oder du schreibst dir selber was, so wie tex das schon angedeutet hat. Solange es nur um Additionen, Subtraktionen, Multiplikationen und Divisionen geht, ist das nicht weiter schwer.
Ich hab das Problem gelöst. Ich hab mir eine etwas einfachere Funktion zur Berechnung der Greenwich-Sternzeit rausgesucht, die aber trotzdem genau ist. Innerhalb dieser hab ich die Konstanten um 10000 normiert, im long-Bereich gerechnet und dann wieder in double gewandelt. Die Genauigkeit liegt im Minutenbereich und das reicht mir. Die nachfolgenden Funktionen können im double-Bereich bleiben, da es sich nur um WInkel im Bereich 0-2PI handelt. Da reichen die signifikanten Stellen aus. Nochmals danke an alle Helferlein.
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.