Forum: Mikrocontroller und Digitale Elektronik ESP32 und C++, Nachkommastellen verschwinden


von Thilo G. (thilo_g)


Lesenswert?

Hallo,
ich versuche gerade für den ESP32 aus der Arduino-IDE heraus eine 
Berechnung zu realisieren. Eigentlich ganz simpel, allerdings werden 
unerwünschterweise die Nachkommastellen gerundet ... kann mir jemand 
einen Tipp geben?

Die Eingangswerte stammen im Byte-Format von der RTC, irgendwo muss ich 
also casten. Unterhalb von "D= ..." sollte die berechnung in einer Zeile 
erfolgen, ich hab das jetzt nur zur Fehlereingrenzung auf Einzelschritte 
gesplittet:
1
float julianDate(byte gYear, byte gMonth, byte gDay, byte gHour, byte gMinute, byte gSecond)
2
{
3
    int Y, M, B;
4
    float D, ret;
5
    float T1, T2, T3, T4, T5;
6
7
    if (gMonth > 2)
8
    {
9
        Y = gYear + 2000;
10
        M = gMonth;
11
    }
12
    else
13
    {
14
        Y = gYear + 2000 - 1;
15
        M = gMonth + 12;
16
    }
17
18
    B = 2 - Y / 100 + Y / 400;        // Ganzzahl!
19
20
    D = gDay + (gHour * 3600 + gMinute * 60 + gSecond) / (float)86400;
21
22
    T1 = floor(365.25*(static_cast<float>(Y)+4716.0));
23
    Serial.printf("T1: %f\r\n", T1);
24
    T2 = floor(30.6001*(static_cast<float>(M)+1.0));
25
    Serial.printf("T2: %f\r\n", T2);
26
    T3 = D;
27
    Serial.printf("T3: %f\r\n", T3);
28
    T4 = static_cast<float>(B);
29
    Serial.printf("T4: %f\r\n", T4);
30
    T5 = 1524.5f;
31
    Serial.printf("T5: %f\r\n", T5);
32
33
    ret = T1;
34
    Serial.printf("ret1: %f\r\n", ret);
35
    ret = ret + T2;
36
    Serial.printf("ret2: %f\r\n", ret);
37
    ret = ret + T3;
38
    Serial.printf("ret3: %f\r\n", ret);
39
    ret = ret + T4;
40
    Serial.printf("ret4: %f\r\n", ret);
41
    ret = ret - T5;
42
    Serial.printf("ret5: %f\r\n", ret);
43
44
    return ret;
45
}

Ein Beispiel der mitgeloggten Zwischenergebnisse:
1
T1:   2460689.000000
2
T2:   183.000000
3
T3:   13.451875
4
T4:   -13.000000
5
T5:   1524.500000
6
ret1: 2460689.000000
7
ret2: 2460872.000000
8
ret3: 2460885.500000
9
ret4: 2460872.500000
10
ret5: 2459348.000000

T3 wird richtig mit reichlich Nachkommastellen berechnet, beim 
Aufsummieren wird plötzlich gerundet, was mache ich da falsch?

Grüße
Thilo

von N. M. (mani)


Lesenswert?

Auch float hat nur einen begrenzten Dynamikbereich den du hier knackst.
Bei ret3 sieht man das gut. Du erwartest was mit vielen 
Nachkommastellen. Durch die große Zahl ist dies allerdings nicht mehr 
möglich, deshalb sind die Nachkommastellen "gerundet".

Gib Mal hier [1] eine Zahl wie 2460885.451875 ein und drück auf Enter. 
Dann siehst du das Problem.

[1]  https://www.h-schmidt.net/FloatConverter/IEEE754.html

: Bearbeitet durch User
von erklehr behr (Gast)


Lesenswert?

Thilo G. schrieb:
> was mache ich da falsch?

Du denkst dass Float beliebig viele Stellen genau ist.

Jetzt erkläre mir bitte was Float Zahlen in Datums-Berechnungen
zu suchen haben.

von Thilo G. (thilo_g)


Lesenswert?

N. M. schrieb:
> Gib Mal hier [1] eine Zahl wie 2460885.451875 ein und drück auf Enter.
> Dann siehst du das Problem.
>
> [1]  https://www.h-schmidt.net/FloatConverter/IEEE754.html

Danke dir, daran hab ich natürlich nicht gedacht ...

Grüße
Thilo

von Michael D. (nospam2000)


Lesenswert?

Thilo G. schrieb:
> T3 wird richtig mit reichlich Nachkommastellen berechnet, beim
> Aufsummieren wird plötzlich gerundet, was mache ich da falsch?

Wie viele signifikante Stellen erwartest du von einer 32-Bit 
Flieskommazahl?

Du hast eine Mantisse von 23 Bit, damit wären Integerzahlen von grob +-4 
Millionen darstellbar.
Die Zahl 2460689 schöpft fast schon das Maximum aus, da bleibt für die 
Nachkommastellen nicht mehr viel übrig, genau gesagt ein Bit und das ist 
genau das was du siehst.

Wenn du genauere Ergebnisse benötigst, dann musst du double anstatt 
float verwenden, nur weiß ich nicht, ob der ESP32 dies überhaupt 
unterstützt.

Alternativ: verwende Festpunkt-Arithmetik.

  Michael

von Thilo G. (thilo_g)


Lesenswert?

erklehr behr schrieb:
> Jetzt erkläre mir bitte was Float Zahlen in Datums-Berechnungen
> zu suchen haben.

Das will mal eine Teleskopnachführung werden, da brauche ich für die 
weiteren Berechnungen das Julianische Datum incl. Tagesbruchteilen. Da 
ich die begrenzte Genauigkeit von float nicht auf dem Schirm hatte, muss 
ich jetzt erstmal sehen, wie ich das sinnvoll löse.

Grüße
Thilo

von Michael D. (nospam2000)


Lesenswert?

Michael D. schrieb:
> Wenn du genauere Ergebnisse benötigst, dann musst du double anstatt
> float verwenden

Du hast Glück, der ESP32 unterstützt wohl tatsächlich double Zahlen: 
https://blog.classycode.com/esp32-floating-point-performance-6e9f6f567a69

  Michael

von Thilo G. (thilo_g)


Lesenswert?

Michael D. schrieb:
> Wenn du genauere Ergebnisse benötigst, dann musst du double anstatt
> float verwenden, nur weiß ich nicht, ob der ESP32 dies überhaupt
> unterstützt.

Ich hab's mal schnell probiert, es funktioniert mit double. Allerdings 
werde ich mir das Timing mal noch in Ruhe ansehen, der ESP32 bekommt ja 
noch deutlich mehr zu rechnen und soll sekündlich aktualisieren. Aber da 
ich jetzt weiß, wo es geklemmt hat, werde ich die nächsten Tage mal den 
kompletten Rechenumfang grob zusammenbauen, um zu sehen, ob er's schafft 
oder ich anders rangehen muss.

Grüße
Thilo

von Wolfgang (Gast)


Lesenswert?

Thilo G. schrieb:
> Das will mal eine Teleskopnachführung werden, da brauche ich für die
> weiteren Berechnungen das Julianische Datum

Warum musst du dich unbedingt auf den 1. Januar −4713 v. Chr. beziehen.
Meist wird das Problem umgangen, indem man nicht das Julianische Datum, 
sondern das Modifizierte Julianische Datum verwendet. Um mehr 
Nachkommastellen zur Verfügung zu haben, gibt es auch noch den Typ 
double

von N. M. (mani)


Lesenswert?

Mit JD*1000 hättest du in einem uint32 bereits 3 Nachkommastellen.
Wenn du mehr brauchst wäre es vllt sinnvoll Ganztage und Tagesbruchteile 
zu trennen und für die Tagesbruchteile eine geeignete Normierung zu 
verwenden.
z.B.
1
struct tJulianDate
2
{
3
uint32_t u32Days;
4
uint16_t u16DayFrac;
5
}

Gekapselt in einer Klasse könntest du auch die Operatoren überladen um 
damit leicht rechnen zu können.

von Michael D. (nospam2000)


Lesenswert?

Thilo G. schrieb:
> werde ich mir das Timing mal noch in Ruhe ansehen, der ESP32 bekommt ja
> noch deutlich mehr zu rechnen und soll sekündlich aktualisieren

Dazu hier noch ein interessanter Artikel, vor allem zu Thema Division 
und den Libraries welche man verwenden sollte:
https://blog.llandsmeer.com/tech/2021/04/08/esp32-s2-fpu.html

Es kann sogar schneller sein, wenn du double anstatt float verwendest.

Ein mal pro Sekunde ist jetzt nicht wirklich eine Herausforderung :-)

 Michael

von Wolfgang (Gast)


Lesenswert?

N. M. schrieb:
> Mit JD*1000 hättest du in einem uint32 bereits 3 Nachkommastellen.

Die Erde dreht sich in einem Tag um 360°. Wenn man da auf etwa eine 
Bogensekunde genau positionieren möchte, braucht man mindestens 6 
Nachkommastellen.

von N. M. (mani)


Lesenswert?

Wolfgang schrieb:
> Die Erde dreht sich in einem Tag um 360°. Wenn man da auf etwa eine
> Bogensekunde genau positionieren möchte, braucht man mindestens 6
> Nachkommastellen.

OK danke für die Info. Die Bogensekunden ist rein Erfahrungswert was man 
für das Fotografieren benötigt? Oder woher kommen die? Rein Interesse 
halber.

von Thomas G. (Firma: Frickelhauptquartier) (taximan)


Lesenswert?

Thilo G. schrieb:
..., da brauche ich für die
> weiteren Berechnungen das Julianische Datum incl. Tagesbruchteilen. Da
> ich die begrenzte Genauigkeit von float nicht auf dem Schirm hatte, muss
> ich jetzt erstmal sehen, wie ich das sinnvoll löse.
>
> Grüße
> Thilo

UNIX-Zeit verwenden?

von thilo_g (Gast)


Lesenswert?

Danke nochmal an alle, ich kenne da nun meine Baustellen und werde mir 
die Lösungsvorschläge in Ruhe ansehen.
Ich will mir letzten Endes nur die Ist-Koordinaten anzeigen lassen - 
umschaltbar in drei unterschiedlichen Koordinatensystemen - um damit 
gezielt Objekte anfahren zu können.
Die erforderliche Auflösung fürs Fotografieren soll der Schrittantrieb 
unabhängig davon erreichen. Da muss dann nur die Interruptfrequenz zur 
Untersetzung incl. Mikroschritten passen.

Einen schönen Feiertag an alle!
Thilo

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.