Float Zahlen mit 10 Nachkommastellen in Tiny GPS Neo 6m Code ???

Hallo Zusammen,

Ich stehe hier vor einem kleinen Rätsel. Habe folgenden Code vor mir:

      int x,y,z;
      double angle;
  
        while(gpsSerial.available()){ // check for gps data
          if(gps.encode(gpsSerial.read())){ // encode gps data
            gps.get_position(&lat_unnormed,&lon_unnormed); // get latitude and longitude
            alt_unnormed = gps.altitude(); 
          }
        }

        alt = alt_unnormed/100.0f;                                          //Numer LEFT of the comma
        lon = lon_unnormed/1000000.0000f;
        lat = lat_unnormed/1000000.0000f;

        Serial.print("lon:  ");
        Serial.print(lon,10);
        Serial.print("   lat: ");
        Serial.print(lat,10);
        Serial.print("   alt:  ");
        Serial.println(alt);

Da ist bei Serial.print(lon,10); und Serial.print(lat,10); tatsächlich ein 10 Kommastelliger Wert auf dem Terminal zu sehen. Ich staune weil ich weiss dass floats nur bis zu 6 Kommastellen kommen, und klar muss es doch nun etwas mit folgender Operation zu tun haben:

lon = lon_unnormed/1000000.0000f;
        lat = lat_unnormed/1000000.0000f;

aber ich weiss wirklich nicht wie das dann im Speicher gemanaget wird. Weil von irgendwo muss der Serial.print befehl ja von den restlichen letzten 4 Kommastellen wissen. Nur zu schade ist dass eine String konvertierung von lon und lat nichts davon weiss, denn da erscheinen nur 6 Kommastellen. Habe gestern erfolglos versucht, daraus Strings zu machen (Muss die lon und lat Werte weiter als Strings verschicken, deswegen).

Nun hoffe ich doch wirklich, ihr könnt mir da Klarheit verschaffen.

Beste Grüsse und vielen Dank

weil ich weiss dass floats nur bis zu 6 Kommastellen kommen

Das ist -so formuliert- falsch.

1 / 100000000.0 liefert 0.0000000100, wenn man sich 10 dezimale Nachkommastellen anzeigen lässt. 100000000.0 und 100000001.0 sind allerdings, als float32, die gleichen Zahlen.

Ein weiterer Irrtum liegt darin, zu glauben, eine Division durch eine Zehnerpotenz sei einfach, weil da nur die Stellung des Kommas verschoben wird. Bei float handelt es sich um Zweierpotenzen, und eine Division durch 10 erzeugt Rundungsfehler. So wie es uns z.B. bei einer Division durch 3 geläufig ist.

float eignet sich nicht für Finanz-Berechnungen, und bei deinen GPS-Koordinatenberechnungen ist es zumindest kritisch, bzw. es können sich lustige Effekte ergeben.

Nachtrag: gps liefert doch eigentlich Text, kannst du den Umweg über float nicht vermeiden?

du kannst bei float 5-6 SIGNIFIKANTE Stellen anzeigen lassen, beginnend ab der höchsten Stelle; der Rest wird als 10er Potenz dargestellt.

0.0000000001 ist einfach 1*10(hoch)-10, mit nur 1 signifikanten Stelle. auch 0.00000000000000000001 geht mit float, das sind dann 1*10(hoch)-20, auch mit nur 1 signifikanten Stelle

0.000000123 wären 3 signifikante Stellen, nämlich 1.23*10(hoch)-7

0.00000000012345 geht auch noch, das wären dann 1.2345*10(hoch)-10, mit 5 signifikanten Stellen

nur wenn du mehr als ca. 6 Stellen hast, also z.B. 0.000000123456789, dann werden die letzten zu Nullen gekürzt, also in etwa zu 0.000000123456000 = 0.000000123456 (die genaue Grenze der Zahl der Stellen-Genauigkeit hängt von der binären Codierung ab, daher kann die max. dezimale Stellenzahl auch mal von 6 abweichen)

die Stellen VOR der mit dem höchsten Wert sind für die Genauigkeit nicht wichtig, da sie in der Zehnerpotenz verschlüsselt werden.

0.00000000012345 wären dann 1.2345*10(hoch)-10 0.0000000012345 wären dann 1.2345*10(hoch)-9 0.00000012345 wären dann 1.2345*10(hoch)-7 0.012345 wären dann 1.2345*10(hoch)-2 1.2345 wären dann 1.2345*10(hoch)0 123.45 wären dann 1.2345*10(hoch)2 12345 wären dann 1.2345*10(hoch)5 123450000 wären dann 1.2345*10(hoch)9

Prinzip klar geworden?

Vielen Dank dsyleixa und auch an michael_x. Kompetente Antworten. Das Adjektiv "signifikant" muss ich wohl beim recherchieren nicht bewusst wahr genommen haben. Soweit so gut, doch ich muss leider sagen, das Rätsel ist noch nicht ganz gelöst, denn bei mir kommen Koordinaten wie 47.8263462784 auf den Serial Monitor, also deutlich mehr als 6 Signifikante stellen. (Da muss ich natürlich auch meine Fragestellung korrigieren, habe ja dort nur von Kommastellen geredet, nicht aber von signifikanten Stellen.) Bin gespannt wie ihr das beurteilt.

kann es sein, dass die 47.8263462784 als Zeichenkette (cstring char[] oder String) dargestellt wird? Wie lautet der genaue Serial.print-Befehl dafür?

lon = lon_unnormed/1000000.0000f;
        lat = lat_unnormed/1000000.0000f;

        Serial.print("lon:  ");
        Serial.print(lon,10);
        Serial.print("   lat: ");
        Serial.print(lat,10);

Nur dieser Abschnitt. Die Werte für lon_unnormed und lat_unnormed (vom Typ long) werden mithilfe von

gps.get_position(&lat_unnormed,&lon_unnormed);

gelesen. lon und lat sind nacvh wie vor floats!

wie und wo sind denn lon und lat definiert bzw. deklariert? welchen Arduino hast du hier?

dsyleixa: kann es sein, dass die 47.8263462784 als Zeichenkette (cstring char[] oder String) dargestellt wird? Wie lautet der genaue Serial.print-Befehl dafür?

print erzeugt auf jeden Fall einen lesbaren Text. Falls der Parameter vom Datetyp float oder double ist, kann man optional einen zweiten Parameter für die Anzahl Nachkommastellen mitgeben (default 2)

Der Fliegendreck am Ende von 47.8263462784 wird übrigens nicht auf ...000 gesetzt, wenn du unbedingt 10 Nachkommastellen sehen willst.

stimmt, die Division z.B. einer ul gibt Fliegendreck am Schluss - simuliert:

void setup() {
  // put your setup code here, to run once:  
  Serial.begin(115200);
  delay(3000); // kurze Wartezeit für Serial
  uint32_t lat_unnormed = 47826346ul;
  float lat;
  lat = (float)lat_unnormed/1000000.0000f;
  Serial.print("   lat: ");     
  Serial.print(lat,10);   // Ausgabe: 47.8263473511

}

void loop() {
  // put your main code here, to run repeatedly:

}

Benutze den Uno und ja sorry hab ich vergessen:

long lat_unnormed, lon_unnormed, alt_unnormed;
float lat,lon,alt; // create variable for latitude and longitude object

Aber wie ist das jetzt mit dem Fliegendreck? Da werden aber nicht etwa vier Zufallszahlen hinten beigefügt? Würde mich nämlich sehr verwundern da das Gps eine viel zu grosse ungenauigkeit hätte...

du kannst bei float eben nur 6 Stellen Genauigkeit erwarten, der Rest ist nunmal "Fliegendreck" - mehr können AVR Arduinos leider nicht. Genau deshalb verwende ich ARMs, z.B. einen Due, einen M0 oder einen M4, denn die können alle mit 64bit double rechnen, dann hat man die gewünschte Genauigkeit.

Aber wie ist das jetzt mit dem Fliegendreck

Das sind zwar keine Zufallsziffern, ist aber trotzdem nicht hilfreich. Schau dir die interne Codierung einer float32 an. Da sind 24 bit der Wertigkeit ...4, 2, 1, 1/2, 1/4, 1/8, 1/16 ... Bei einer Zahl zwischen 32 und 64 (wie in deinem Beispiel) hat das niederwertigste Bit den Wert 2^-19. =0.0000019073 Springt also in Stufen von ca. 0.000002, liefert aber in Dezimal Schreibweise 10stelligen Fliegendreck.

Nicht ohne Grund liefert deine Library keine float32 sondern long integer. (ohne Rundungsfehler)

Nachtrag: Trenne die Werte in Grad und Minuten oder (genauer) Minuten und Sekunden. Da reicht dann float32 für die Sekunden, oder auch schon für die Minuten.

Außer den schon genannten Möglichkeiten könntest Du auch einen Teensy 3.5 oder 3.6 verwenden, die rechnen mit 32 Bit und haben sogar eine Fließkommarecheneinheit drauf. Die Programmierung geht auch über die Arduino IDE (teensyduino).

Da Du einen Intergerwert geliefert bekommst, könntest Du auch mit Integerarithmetik rechnen. Auf einem UNO habe ich das noch nicht gesehen, sollte aber funktionieren.

Teensy 3.5 und 3.6 gehören ja zu den von mir genannten ARM Cortex M4. Eine Alternative wäre, die raw Zahl durch int 100 zu dividieren, dann hat man die Grad, und das Modulo (der Divisions-Rest) wäre eine 6-stellige Integer-Zahl, die man dann verlustfrei auch in float konvertieren kann: das wären dann quasi nur die Nachkommastellen für Minute und Sek. samt 100stel-Sek, und die lassen sich dann auch per Div. durch 60 weiter auftrennen. Besser finde ich aber nach wie vor double auf ARMs.