Arduino verrechnet sich?!

Hallo,

ich habe ein Problem, an dem ich mir die Zähne ausbeiße und nicht weiterkomme.

Folgendes: In einem Menue soll ein float wert mit 4 Nachkommastellen eingegeben werden können, und zwar so, dass die einzelne stelle mit dem Taster hochgezählt werden kann.
Das funktioniert auch alles, aber bei der letzten Stelle verrechnet er sich immer wieder.

Ich konnte den kritischen Teil in einen extra sketch extrahieren, der das selbe Fehlverhalten zeigt:

double _Wert = 0.0007;
void setup(){
  Serial.begin(57600);  
}
void loop(){  
     byte _Fliess_0001= long(_Wert *10000.0)  % 10;         // die vierte Nachkommastelle wird separiert als Byte.    
     Serial.print("Anfangs-Wert:                 ");Serial.println(_Wert,8);
     Serial.print("4. Nachkommastelle separiert:      ");Serial.print(_Fliess_0001);Serial.println();

      // wieder zu einer double Zahl zusammenfügen
      _Wert = _Fliess_0001 * 0.0001;
            
      double a = _Wert *10000.0;      // eine Kontrollrechnung fürs debuggen, um den Fehler zu finden.
      

     Serial.print("  neuer Wert                  ");Serial.println(_Wert,8);
     Serial.println("   Kontrollrechnung: ");Serial.print(_Wert,8);Serial.print(" * 10000:                ");Serial.print(a,8);
    Serial.println();Serial.println();
     delay(2000);
}

Was passiert?

Ausgabe auf dem Display:

Anfangs-Wert:                 0.00070000
4. Nachkommastelle separiert:      6
  neuer Wert                  0.00060000

Die "7" wird gewandelt in eine "6"!

Die Kontrollrechnung bringt es an den Tag:

Kontrollrechnung: 
0.00060000 * 10000:                5.99999952

die 5.999999 wird beim Wandeln in integer gewandelt in 5.

Kann mir jemand sagen, wie ich das weg kriege? ein Runden mit float auf die 4.Nachkommastelle wäre eine Option, aber einfach überzogen!

Grüße

Gunther

edit: Arduino UNO

Arduino verrechent sich nicht; Du kennst Dich nicht mit Float-Variablen aus.
Float-Variablen haben zwar einen großen Wertebereich den sie abbilden können, aber nur 6-7 gültige Stellen. Jede Stelle mehr ist zufällig.
Wenn Du mehr Genaugigkeit brauchst mußt Du dir eine Long Zahl mit einem Komma an der 4, 5, 6 ecc Stelle vorstellen (indem fürs rechnen mit 10000 100000 ecc multipliziert wird) oder Kommazahl aus 2 INT oder LONG Variablen zusammenbasteln.

http://arduino.cc/en/Reference/Float

Grüße Uwe

Du hast recht was float anbelangt.

Deshalb habe ich ja extra auch double genommen, was nach IEEE-754 eigentlich 15 dezimalstellen sicher abbilden können sollte.

Und ausserdem sollten 0.0007 in float doch eigentlich als 7 * 10^-4 abgebildet werden, oder?

Gibt es eine Möglichkeit, die hinteren Stellen zu runden?

Gunther

guntherb:
Deshalb habe ich ja extra auch double genommen, was nach IEEE-754 eigentlich 15 dezimalstellen sicher abbilden können sollte.

Hehe, aber nicht bei den 8-Bit Controllern.

Checke bitte mal kurz die tatsächliche Datentypgröße:
Serial.begin(9600);
Serial.println(sizeof(float));
Serial.println(sizeof(double));

Dann kommst Du drauf:
Bei allen Arduino 8-Bit Controllern ist double=float= 4 Bytes
Double ist zwar deklarierbar, aber tatsächlich - ein float!

Nur beim 32-Bit Controller Arduino DUE hast Du als Datentyp double tatsächlich ein double mit 8 Bytes!

http://arduino.cc/en/Reference/Double

Das nutzt aber alles nichts. Bei floats wird die Mantisse normalerweise binär abgelegt --> nicht einmal 0.1 kann genau dargestellt werden. Wenn man 4 Nachkommastellen braucht, dann nimmt man eben Integers und nimmt einfach die letzten 4 Stellen als Nachkommastellen. Alles Andere ist ziemlicher Pfusch und funktioniert am Ende doch nicht richtrig.

ok, ihr habt mich überzeugt.

letztlich habe ich das ganze ja nur in double gemacht, weil es sich hierbei um die Parameter für die PID library handel, die ja in Double haben wollen.
Ich werde die Variablen also in Integer halten, und erst kurz vor der Übergabe an den PID in double konvertieren.

Danke für die Hilfe

Gunther

Nach längerer Überlegung habe ich beschlossen, das Ganze doch nicht über Integer zu lösen, weil ich sonst alles komplett umbauen müsste.

Das Problem liegt ja in der Rechnung:

double a = 0.0007 * 10000;
wobei "a" nachher den Wert 6,9999 annimmt.
Und beim anschliessenden Wandeln in int steht dann da eine "6" wo ich eigentlich eine "7" brauche.

Ich habe das Problem jetzt durch einen (nicht ganz sauberen) Trick gelöst:
double a = 0.0007 * 10000 + 0.001;
dadurch bekomme ich ein Ergebnis, das auf alle Fälle eine "7" vor dem Komma stehen hat, und beim Wandeln in int fällt der Rest sowieso weg.

Aber ich hatte wirklich nicht auf dem Radar, das float und double beim Arduino das Selbe sind!

Danke für die Hilfe!

Gunther

Wenn Du schon runden willst, dann addiere 0,5.
Grüße Uwe