Probleme bei Multiplikation...

Ein freundliches Hallo an alle,
vielleicht kann mir ja jemand von euch weiter helfen...

wenn ich z.B. 0.09 * 1000 rechne um auf 90 zu kommen funktioniert das ja an sich so :

float x = 0.09;

void setup() {
  Serial.begin(9600);
}

void loop() {
  
  float w = x * 1000.0;
  
  Serial.print("w = ");
  Serial.println(w);
  Serial.println("--------");
  delay(1000);
}

wenn ich allerdings eine Vorrechnung ( die nötig ist) durch führe um auf 0.09 zu kommen um dann 0.09 * 1000 rechnen zu können, kommt aus irgend einem Grund das Programm auf 89.97 mit diesem Code:

float x = 1360;
float y = 1360.09;

void setup() {
  Serial.begin(9600);
}

void loop() {
  
  float w = (y - x)*1000.0;
  
  Serial.print("w = ");
  Serial.println(w);
  Serial.println("--------");
  delay(1000);
}

Nun meine Frage: Warum kommt auf dem weg 89.97 raus ?! Ich weiß langsam nicht mehr weiter .. ich hab auch schon probiert das in mehrere rechnungen zu unterteilen hat aber auch nicht funktioniert.

Typische Rundungsfehler bei Gleitkommazahlen. Du hast nur eine begrenzte Anzahl an Werten um eine viel größere Anzahl an Zahlen darzustellen.

Anschaulich z.B. mit diesem Tool:
https://www.h-schmidt.net/FloatConverter/IEEE754de.html

stimmt, du hast bei float nur ca. 6 signifikante Dezimalstellen, und du hast hier ja bereits die 6-Stellen-Grenze erreicht:
1360.09

dadurch kommt es zu den von Serenifly genannten Rundungs- bzw. Präzisionsfehlern.

Wenn du mehr als 6 Stellen Genauigkeit brauchst, musst du 64bit double verwenden - die gibt es aber nicht auf AVRs, sondern nur auf ARMs (Zero, Due, M4) oder ESPs (esp8266, esp32).

(edit: auf AVRs sind double nur dasselbe wie die 32bit float)

Danke für die schnellen, hilfreichen Antworten!

Das erklärt natürlich einiges... wäre es denn ein möglicher Lösungsansatz zu sagen, man löscht die 6 stellige zahl nachdem man die 0.09 ausgerechnet hat, um die 32bit wieder frei zu bekommen, denn wenn nur 0.09 im float, laut der Seite, gespeichert werden ist der Rundungsfehler gering genug damit 90 raus kommt wenn man * 1000 rechnet?

Für dein Bsp würde ich float w = (y100 - x100)*10; versuchen.
Ob das fachlich io ist und dir nicht irgendwann um die Ohren fliegt vermag ich dir aber nicht zu sagen.

@Thorsten4171 auch an dich ein großes Danke!

die Gleichung klappt super! Es gibt zwar noch ein paar Ungenauigkeiten wenn man unter 0.01 geht aber die sind mir gering genug und verkraftbar, da die zahlen drüber genau sind. Außerdem glaube ich, ohne auf ein anderes Board umzusteigen bekommt man das nicht mehr genauer hin wenn, ich die Antwort von @dsyleixa richtig verstanden hab.
Aber nichts desto trotz wieder was dazu gelernt und die Seite von dem Tool wurde schon gespeichert.

Nochmal ein großes Danke an alle für die schnelle lösung!

Tatsächlich ist int32 genauer als float32.
In deinem Beispiel würde

uint32_t x = 1360000;
uint32_t y = 1360090;
float w = y-x; // exakt 90.00

funktionieren.
Int-Arithmetik hat andere Tücken (Überlauf/ Unterlauf) aber keine Rundungsfehler.
Im Beispiel ist ist die Anzeige 90.00 natürlich Fake. Das Ergebnis ist die Zahl zwischen 89 und 91.

Hallo,

da musste ich erst mal wieder in ganz alten Erinnerungen kramen. Vor langer, langer Zeit hab ich auch viel Siemens S5 gemacht. die konnte teilweise auch keine Gleikomma Berechnung. Also hat man erst mal alles auf die erforderliche Auflösung umgerechnet z.B 20.2 wurden als 202 dargestellt. Damit konnte man dann mit ganzen Zahlen rechnen. Natürlich muss man die max Darstellungsgrenze beachten. Das war damals Doppelwort also 32Bit, natürlich kam da auch schon mal Mist raus wegen Überlauf.

wenn man das auf deinen Sketch umsetzt kommt das raus und dann stimmt auch die Rechnung.

Gruß Heinz

float x = 1360;
float y = 1360.09;


void setup() {
  Serial.begin(9600);
}

void loop() {
  long xi = x * 100;// Umrechnung auf long 2 Nachkomma Stellen
  long yi = y * 100;  
  long a = (yi - xi) * 1000;// Ganzzahl Berechnung machen
  float w = a / 100;// anschliessend wieder zurück



 Serial.print("w = ");
  Serial.println(w);
  Serial.println("--------");
  delay(1000);
}