Forum: Mikrocontroller und Digitale Elektronik Nachkommastellen Arduino


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


Lesenswert?

Moin zusammen,
ich hab hier ein STM32 F103 den ich mit der Arduino-IDE programmiere.
Für astronomische Berechnungen brauche ich folgende Formel:
double eps;
eps=279.6966778+36000.76892*ta+0.0003025*(ta*ta);  //ta=1.20658453

Der STM liefert als Ergebnis: 43717.668 006 321 1;
Wolfram Alpha: 43717.667 965 170 291 580....  (Taschenrechner auch)
(Ich nehme mal an, dass WA genauer ist...)

Ich war bis dahin der Meinung, dass die Verwendung eines STM zur 
32-Bitrechnerei ausreichend sei. Vermutlich hab ich mich dann doch 
getäuscht. Wo liegt mein Fehler.

Thomas

von g457 (Gast)


Lesenswert?

> Ich war bis dahin der Meinung, dass die Verwendung eines STM zur
> 32-Bitrechnerei ausreichend sei.

Ist er auch, aber für die von Dir gewünschte Genauigkeit wirst Du echte 
double (mit 64 bit) brauchen. Auch dafür reicht jener stm32 locker. 
Deine Zahlenbeispiele legen allerdings nahe, dass Du nur mit 32-bit 
floats rechnest.

HTH

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


Lesenswert?

Hmmmm, und wie stelle ich das an?

von Johannes S. (Gast)


Lesenswert?

Das sollten schon double sein. Beim Compiler gibt es eine Option die 
Konstanten als float zu betrachten, das müsste dann in den Compiler 
Aufrufen zu sehen sein. Ist aber eine Holzhammermethode und eher 
unschön. Aber in vielem AVR Arduino code wird einfach immer double 
genommen und das produziert beim ARM unnötig viel/langsamen Code. 
Vielleicht ist deshalb so eine Option gesetzt?

von Wolfgang (Gast)


Lesenswert?

Thomas G. schrieb:
> Ich war bis dahin der Meinung, dass die Verwendung eines STM zur
> 32-Bitrechnerei ausreichend sei. Vermutlich hab ich mich dann doch
> getäuscht. Wo liegt mein Fehler.

Bei 32-Bit hast du im IEEE 754 binary32 Format (Single Precision) nur 24 
Bit oder 7.22 Digits. Wenn du dann Additionen mit so verschieden großen 
Zahlen machst, gehen dir die Nachkommastellen flöten.
> eps=279.6966778+36000.76892*ta+0.0003025*(ta*ta);  //ta=1.20658453
wird zu
1
eps = 279.6966778 + 43437.97095 + 0.000440393
und weiter ungefähr zu
1
eps = 279.70 + 43437.97 + 0.00

Wenn du es genau wissen willst, musst du es binär hin schreiben.
https://en.wikipedia.org/wiki/IEEE_754#Basic_and_interchange_formats

von Johannes S. (Gast)


Lesenswert?

Nur eps ist schon als double deklariert und die Konstanten sind es auch, 
wenn eben nicht per Compiler Option umgebogen.

von Wolfgang (Gast)


Lesenswert?

Johannes S. schrieb:
> Nur eps ist schon als double deklariert und die Konstanten sind es auch,
> wenn eben nicht per Compiler Option umgebogen.

Damit die "double" wirklich double sind, darf der Compiler dann aber 
keine 32-Bitrechnerei machen (wie es z.B. auf einem AVR passiert), 
sondern muss wirklich 64 Bit für die Zahlen verwenden.

von Johannes S. (Gast)


Lesenswert?

Wolfgang schrieb:
> sondern muss wirklich 64 Bit für die Zahlen verwenden.

Ja, und das macht die newlib die beim gcc für die Cortex-M verwendet 
wird.

Thomas G. schrieb:
> Der STM liefert als Ergebnis:

Und wie wird das ausgegeben? Evtl beschneidet das Arduino Print das 
Ergebnis.

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


Lesenswert?

Johannes S. schrieb:

> Und wie wird das ausgegeben? Evtl beschneidet das Arduino Print das
> Ergebnis.

Die ersten beiden Nachkommastellen passen, die dritte evtl als gerundet. 
Danach passt nichts mehr.

von Johannes S. (Gast)


Lesenswert?

Ich meinte, mit welcher Methode kontrollierst du das Ergebnis? Debugger?

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


Lesenswert?

öhm, ich weiss nicht genau was du meinst;
Serial.print("eps= ");
Serial.println(eps,15);

Meintest du so etwas?

von Εrnst B. (ernst)


Lesenswert?

Thomas G. schrieb:
> Die ersten beiden Nachkommastellen passen, die dritte evtl als gerundet.
> Danach passt nichts mehr.

Wie weiter oben geschrieben: Bei 32-Bit-Fließkommazahlen hast du 24 Bit 
(= ca. 7 Dezimalstellen) für die Ziffern selber, die restlichen Bits 
definieren "wo das Komma hinkommt".

Du hast 5 Stellen vor dem Komma und zwei danach genau. Passt also exakt 
auf die 32-Bit-Float Theorie.

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


Lesenswert?

Das war mir vorher auch so in etwa klar, ich war jedoch aus mangelnder 
Erfahrung der Meinung, dass 'double' in Verbindung mit dem STM dieses 
Problem umgeht.

von Εrnst B. (ernst)


Lesenswert?

Thomas G. schrieb:
> dass 'double' in Verbindung mit dem STM dieses
> Problem umgeht.

dann zeig mal den Code, wo du das "double" hingeschrieben hast.
das "double eps;" hat z.B. keine Wirkung auf die Berechnung, wenn "ta" 
als float (mit 32 Bit) definiert ist.

von Johannes S. (Gast)


Lesenswert?

Thomas G. schrieb:
> dass 'double' in Verbindung mit dem STM dieses
> Problem umgeht.

das ist auch richtig. Also macht das Print etwas falsch oder es wird mit 
Compileroptionen AVR kastriert. Was eigentlich Quatsch ist.

Kennt der Arduino Core (welchen benutzt du?) ein
1
Serial.printf("eps=%f\n", eps);
? Beim STMDuino sollte das gehen.

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


Lesenswert?

Mit dem Befehl kann der Compiler nichts anfangen "
no matching function for call to 'print(const char [8], double&)'"

printf ist auch unbekannt
Arduino: 1.8.12 (Windows 10), Board: "Arduino Uno WiFi Rev2, ATMEGA328" 
<- natürlich Quatsch, stm ist eingestellt
Core dürfte 1.8.3 sein.

ta und eps sind beide im Header als double deklariert.

: Bearbeitet durch User
von Wolfgang (Gast)


Lesenswert?

Thomas G. schrieb:
> Das war mir vorher auch so in etwa klar, ich war jedoch aus mangelnder
> Erfahrung der Meinung, dass 'double' in Verbindung mit dem STM dieses
> Problem umgeht.

Dann prüfe es nach.
Wie klein darf eps werden, ohne dass 1 + eps == 1 ist?

von Johannes S. (Gast)


Lesenswert?

Thomas G. schrieb:
> Core dürfte 1.8.3 sein.

Dann wird das der Roger Clark core sein. Im PrintFloat steht als 
Kommentar:
1
/* THIS FUNCTION SHOULDN'T BE USED IF YOU NEED ACCURATE RESULTS.
2
 *
3
 * This implementation is meant to be simple and not occupy too much
4
 * code size.  However, printing floating point values accurately is a
5
 * subtle task, best left to a well-tested library function.
6
 *
7
 * See Steele and White 2003 for more details:
8
 *
9
 * http://kurtstephens.com/files/p372-steele.pdf
10
 */
11
size_t Print::printFloat(double number, uint8 digits) {
https://github.com/rogerclarkmelbourne/Arduino_STM32/blob/master/STM32F1/cores/maple/Print.cpp
Ob der Algorithmus die double Nachkommas richtig ausgibt weiss ich 
nicht.


probiere es mit dem STM32duino core, der sollte das Serial.printf 
unterstützen.

oder per sprintf() in einen buffer schreiben und dann ausgeben.

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


Angehängte Dateien:

Lesenswert?

Also, um die Katze aus dem Sack zu lassen, dass Ganze ist nur ein 
kleiner Zwischenschritt um den Sonnenauf-/untergang zu berechnen. eps 
wird sich ziemlich genau um diesen Wert bewegen. Dabei sind die 
Nachkommastellen das kriegsentscheidende. Wem es weiterhilft, ich habe 
mal den Code angefügt. Interessant wird es ab //47.
PI aus math.h wird übrigens mit gewünschter Genauigkeit angezeigt. Für 
mich heute aber nicht mehr. Ich danke euch bis hierhin.

von Wolfgang (Gast)


Lesenswert?

Johannes S. schrieb:
> oder per sprintf() in einen buffer schreiben und dann ausgeben.

Das ist doch alles ein Nebenschauplatz. Für irgendwelche Berechnungen 
sollte es doch völlig wurscht sein, mit wieviel gültigen Stelle 
sprintf(), Serial.printf() oder sonst eine Ausgabe daher kommt.
Hauptsache ist, dass die Berechnung mit der erwarteten Stellenzahl 
läuft.

von Johannes S. (Gast)


Lesenswert?

Thomas G. schrieb:
> Für
> mich heute aber nicht mehr.

für mich auch, ich hätte einen µC mit in den Urlaub nehmen sollen :)

Aber auch ein CM0 rechnet doubles mit 64 Bit. Das ist ja die Crux wenn 
man eine Arduino Lib portiert und irgendeine Konstante ohne 'f' suffix 
drin hat, dann sprengt es gleich den Flash.

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

ein virtuelles Target habe ich, da läuft ein cross compiler der die 
Mathematik richtig emulieren sollte. Auf dem kommt in Mbed das richtige 
raus.
https://simulator.mbed.com/#user_1599113530283


Die Größe von float/double kann man ja mal den Compiler anzeigen lassen, 
da sollte auch 4/8/8 rauskommen:
1
    Serial.println( sizeof(float) );
2
    Serial.println( sizeof(double) );
3
    Serial.println( sizeof(1.20658453) );
Wenn nicht, dann ist irgendwo versteckt etwas verbogen. Per default 
rechnet der gcc mit ARM eabi mit double.

von Johannes S. (Gast)


Angehängte Dateien:

Lesenswert?

ich habe mal das ganze Programm umgeschrieben damit es im Simulator 
läuft, da sieht man das die Berechnung korrekt ist.
Der 'Fehler' liegt in der Genauigkeit von ta, das in der 
Vergleichsrechnung aus dem Kommentar benutzte hat eine reduzierte 
gegenüber dem was das Programm berechnet.
Das Programm rechnet also korrekt mit 64 Bit FP.
1
sizeof(float) = 4
2
sizeof(double) = 8
3
sizeof(1.20658453) = 8
4
ta =    1.206584530000000
5
eps = 43717.667965170294337
6
Los geht's!
7
JD von:
8
29 8 2020 2459090
9
obli   23.436605537634907
10
ta=    1.206584531143053
11
eps= 43717.668006321073335
12
PI=    3.141592653589793
13
w=  283.295833576472546
14
exc=    0.016700421329973
15
m0=    4.090566089446199
16
0
17
a-start
18
exzentrizität=  233.606008468437892
19
  -2.012743228152881
20
Sun's geocentric ecliptic longitude=  156.135364408051487
21
declin=    9.259962048874758
22
0
23
0
24
-+alpha'  157.907617821615673
25
alpha std'   10.527174521441045
26
0
27
Stundenwinkel=    6.866390897696414
28
LSTr    3.660783623744631LSTs   17.393565419137460
29
15. LST - GST
30
GSTr    3.153531357077965
31
GSTs   16.886313152470795
32
13 gst ut
33
2459090
34
a (s) 7545
35
b (t) 0
36
t0 1. 502
37
t0 4.22
38
6.1.    0.000000000000000
39
6.2.    0.000000000000000
40
UT  -19.304448552388347
41
13 gst ut
42
2459090
43
a (s) 7545
44
b (t) 0
45
t0 1. 502
46
t0 4.22
47
6.1.  -19.304448552388347
48
6.2.  -19.304448552388347
49
UT   -5.609163207204407
50
UTr  -19.304448552388347
51
UTs   -5.609163207204407
52
lctr  -17.304448552388347
53
LJDr 2459089.778981310315430
54
jd 5.
55
ljdr= 2459090
56
i 2459090
57
f 0
58
a 16
59
b 2459103
60
c 2460627
61
d 6736
62
ee 233
63
g 9
64
  28.278981310315430 8 2020
65
Sonnenaufgang:    6.695551447570324
66
   6.000000000000000:  41.733085632324219:43
67
AZIrise=   74.098783190323033
68
Sonnenuntergang: AZIset=  285.901216809676953
69
  19.060081120645130END END

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


Lesenswert?

@von Johannes S.
ich habe mal das ganze Programm umgeschrieben damit es im Simulator
läuft, - das ist nett von dir!

da sieht man das die Berechnung korrekt ist. - das beruhigt mich.
Der 'Fehler' liegt in der Genauigkeit von ta, - ?

das in der
Vergleichsrechnung aus dem Kommentar benutzte hat eine reduzierte
gegenüber dem was das Programm berechnet.
Das Programm rechnet also korrekt mit 64 Bit FP. - s.o.

Dann will ich mal sehen, dass ich das genauer hin bekomme. Das da schon 
der Fehler liegt, wäre ich ohne Tip nicht drauf gekommen.

Bei der Angabe ta handelt sich übrigens um die "Anzahl der Jahrhunderte" 
seit 0.1.1900. (ist eine Rechenhilfe der Astronomen).

Ich hab mir mal auf die Schnelle einen DUE geholt (braucht man immer 
mal), aber selbes (falsches) Ergebnis :(

von Joachim B. (jar)


Lesenswert?

Thomas G. schrieb:
> dass Ganze ist nur ein
> kleiner Zwischenschritt um den Sonnenauf-/untergang zu berechnen

hmmm, die normale Arduinogenauigkeit reicht aber absolut dafür sogar auf 
dem nano, bei mir seit 2015 für meine Rolladen automatisiert zu fahren.
Je nach Rechenmodell wechselt die Genauigkeit nur zwischen Küche, 
Wohnzimmer und Schlafzimmer.

Ich denke ob double oder höher macht nichts dafür, aber mag sein das es 
einige genauer wünschen.

: Bearbeitet durch User
von Thomas G. (Firma: Frickelhauptquartier) (taximan)


Lesenswert?

Also, die Formeln und der Rechenweg geben eine Genauigkeit von ca 1 min 
her.

von Joachim B. (jar)


Lesenswert?

Thomas G. schrieb:
> Also, die Formeln und der Rechenweg geben eine Genauigkeit von ca 1 min
> her

und das reicht dir oder nicht?

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


Lesenswert?

Wenn ich dahin gekommen wäre, würde ich hier nicht nachfragen. Im Moment 
schwankt die Ungenauigkeit abhängig vom Datum um 1 - 5 Minuten (hab 
nicht jedes Datum ausprobiert), das ist zumindest unschön.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Bei der Verwendung von Extended-Datentypen wie z.B. "long double" sollte 
man unbedingt darauf achten, auch bei numerischen Literalen den 
entsprechenden Datentyp anzugeben. Ich habe mal das folgende kleine 
Progrämmchen auf einem Linux-Rechner ausprobiert:
1
#include <stdio.h>
2
3
int main(int argc, char **argv) {
4
  float       f;
5
  double      d;
6
  long double e;
7
  long double ee;
8
9
  float       tf  = 1.20658453;
10
  double      td  = 1.20658453;
11
  long double tdd = 1.20658453l;
12
13
  f   = 279.6966778  + 36000.76892  * tf  + 0.0003025  * (tf * tf);
14
  d   = 279.6966778  + 36000.76892  * td  + 0.0003025  * (td * td);
15
  e   = 279.6966778  + 36000.76892  * tdd + 0.0003025  * (tdd * tdd);
16
  ee  = 279.6966778l + 36000.76892l * tdd + 0.0003025l * (tdd * tdd);
17
18
  printf("f  = %30.25f\n", f);
19
  printf("d  = %30.25f\n", d);
20
  printf("e  = %30.25Lf\n", e);
21
  printf("ee = %30.25Lf\n", ee);
22
23
  return 0;
24
}

Hierbei gab es dann die folgenden Ergebnisse:
1
f  = 43717.6679687500000000000000000
2
d  = 43717.6679651702943374402821064
3
e  = 43717.6679651702942770441495668
4
ee = 43717.6679651702915840871810360

Es machte keinen Unterschied, ob ich das Programm mittels "gcc" oder 
"gcc -m32" kompilierte.

von Johannes S. (Gast)


Lesenswert?

ich habe auch eine Rolladensteuerung, die Zeiten lasse ich aber zentral 
von einem SunEvents Node in NodeRed berechnen:
https://github.com/freakent/node-red-contrib-sunevents
bzw. der benutzt SunCalc:
https://github.com/mourner/suncalc/blob/master/suncalc.js

Ist JavaScript, aber die Mathe ist ja sehr C++ ähnlich und JS rechnet 
auch mit double. Das sollte sich also auch relativ leicht nach C++ 
konvertieren lassen. Bzw. vielleicht gibt es das auch schon auf github.

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


Lesenswert?

Johannes S. schrieb:
> die Zeiten lasse ich aber zentral
> von einem SunEvents Node in NodeRed berechnen:

genau das wollte ich vermeiden. Es sollte eine StandAlone-Lösung werden.

von Hermann-Josef (Gast)


Lesenswert?

Hallo,

wie wäre es, Sonnenauf- und Untergangszeiten einmal für ein ganzes Jahr 
zu berechnen, und dann als Tabelle (für jeden Tag 2 Zahlen, jeweils 
Minuten nach Mitternacht) abzuspeichern?

So habe ich es bei meiner Beckhoff-Steuerung in ST gemacht. Ich denke, 
der Fehler über die Jahre wird nicht sehr groß werden.

Man könnte die Berechnung auch mit einer vereinfachten Formel (Link) 
machen, angeblich ist der Fehler für Deutschland absolut unter 2 
Minuten. Damit kann ich leben.

https://lexikon.astronomie.info/zeitgleichung/

- Hermann-Josef

von Joachim B. (jar)


Lesenswert?

Thomas G. schrieb:
> Im Moment
> schwankt die Ungenauigkeit abhängig vom Datum um 1 - 5 Minuten (hab
> nicht jedes Datum ausprobiert), das ist zumindest unschön.

bei meiner Rolladensteuerung stört das nicht, es ist ja auch immer 
verschieden hell je nach Wolken am Himmel, also wenn dann hilft das nur 
mit einem LDR draussen!
Momentan stören mich die Minuten nicht die die Rolläden zu früh oder zu 
spät fahren in Abhängigkeit der Helligkeit!

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.