Metatrader Forum | Forex Expert-Advisor | Broker & Forex Tools

Metatrader Forum | Forex Expert-Advisor | Broker & Forex Tools (http://www.expert-advisor.com/forum/index.php)
-   Programmierung MQL5 (http://www.expert-advisor.com/forum/forumdisplay.php?f=221)
-   -   Subtraktionsrest bei double-Zahlen (http://www.expert-advisor.com/forum/showthread.php?t=7332)

traderdoc 06.06.23 20:46

Subtraktionsrest bei double-Zahlen
 
Dass die Divison von Zahlen manchmal Werte mit 10 und mehr Kommastellen ergeben, z.B.

0.6/0.2 = 2.9999999999999996

ist bekannt, wenn auch unschön und hat mich so manches graues Haar gekostet.

Aber nun toppt das das o.g. noch um die Tatsache, dass auch die Subtraktion zweier double-Zahlen folgendes ergibt:

0.6 - 0.2 = 0.39999999999999997

Wer kann mir diesen Blödsinn sinnhaft erklären?

D.h. bei solch einer simplen Subtraktion müsste ich zum Erhalt der korrekten Differenz ein NormalizeDouble(0.6-0.2, 1) formulieren!!

Auch ein

MathMod(0.3-0.1, 0.1)

sollte eigentlich 0 ergeben.
Die reale Zahl dieser Operation beträgt aber 0.09999999999999998.

Das kann es doch aber nicht sein!

traderdoc

traderdoc 06.06.23 21:40

Und nun wird es immer abenteuerlicher:

Bei den double-Variablen:
Lot_Max_Start = 0.1
Lot_Max_End = 0.3
Lot_Max_Step = 0.1

und

for (double lot_max = Lot_Max_Start; lot_max <= Lot_Max_End; lot_max += Lot_Max_Step) {
Print(lot_max);
}
Print(lot_max);

sollte eigentlich in den Printausgaben

0.1
0.2
0.3

stehen und was steht tatsächlich?

0.1
0.2
0.30000000000000004

Damit wird die o.g. Schleife nicht dreimal, sondern nur zweimal durchlaufen.

Was ist hier los???

traderdoc

Indikator-Trading 07.06.23 07:31

Die Anzahl an Schleifendurchläufe machst du besser immer mit Integer. Ansonsten kannst du NormalizeDouble() nutzen, damit du immer sicher auf eine bestimmte Nachkommastelle rundest.

traderdoc 07.06.23 16:30

Ja, das ist mir schon klar, danke.
Aber wieso kann MQL5 double-Zahlen nicht ordentlich subtrahieren bzw. auch addieren, denn bei 0.1 + 0.2 + 0.3 kommt eben nicht exakt 0.3 raus, sondern 0.30000000000000004. Das mag häufig unerheblich sein, aber gerade bei if-Abfragen mit Vergleichswerten gibt es z.T. keien korrekten Entscheidungen.

Das kann es doch nicht sein, dass man bei jeder double-Operation NormalizeDouble() benutzen muss, zumal manchmal nicht immer die Kommastelle
bekannt ist.

traderdoc

Ca$hDigger 08.06.23 16:40

Hallo traderdoc damit muss man leben und es ist (leider) alles normal denn Gleitkommazahlen können nicht präzise dargestellt werden, da sie in Computerarithmetik als binäre Zahlen gespeichert werden müssen. Die meisten Gleitkommazahlen können nicht direkt als Binärzahl mit endlicher Länge dargestellt werden, was dazu führt, dass eine binäre Gleitkommazahl nur eine Annäherung an die tatsächliche Dezimalzahl ist. Dies führt zu Rundungsfehlern und Ungenauigkeiten bei der Durchführung von Berechnungen mit Gleitkommazahlen. Zudem gibt es auch teilweise Limitierungen der Anzahl der Bits, die zur Darstellung von Gleitkommazahlen zur Verfügung stehen.
Genau genommen bringt NormalizeDouble() auch nur wieder ein double als Rückgabewert und ist kein "genauer" Integer und damit potentiell "ungenau". Gerade in Schleifen niemals eine Gleitkommazahl nutzen!, immer zuvor ein Casting/Typumwandlung machen zu einem Integer.
Gruß

traderdoc 08.06.23 20:07

Aber was nutzt mir der Integer in einer Schleife als Schleifenzähler, wenn ich dann die double-Zahlen aufaddieren muss und bei 0.1 + 0.1 + 0.1 eben nicht 0.3, sondern 0.30000000000000004 rauskommt und nach Abfrage, ob dieser Wert <= 0.3 ist mir dann ein false gibt und die Schleife zwar 3mal angefahren wird, aber mit der if-Abfrage die 3. Schleife doch nicht durchläuft.
Die Benutzung von NormalizDouble(), v.a. bei Divisionen mache ich doch schon seit anfang an, aber bei einer Addition bzw. Subtraktion hätte ich nicht vermutet, dass das eine krumme Zahl ergibt.

Das ist alles frustrierend.

traderdoc

Ca$hDigger 08.06.23 23:16

Zitat:

Zitat von traderdoc (Beitrag 46550)
aber bei einer Addition bzw. Subtraktion hätte ich nicht vermutet, dass das eine krumme Zahl ergibt.

Das ist normal und gilt für alle Rechnungen. Sobald man aus einer Rechnung ein Ergebnis erhält hat man es direkt mit diesen natürlichen Ungenauigkeiten zu tun und muss damit umgehen.

Ich habe erst auf den zweiten Blick gesehen was alles in der Schleife los ist, dann ist die Idee mit dem double dort nachvollziehbar aber es sind dann da sogar zwei Gleitkomma Aspekte :
1. Ergebnisse aus Rechnungen: kann man wie gehabt mit NormalizeDouble() glatt bügeln.
2. Vergleiche: der <= trägt neben der kleinerAls auch eine isEqual Logik in sich, dies auch beachten, denn der check ob zwei Gleitkommazahlen gleich sind, ist auch eine Stolperfalle. Ein Abgleich wie isEqual oder auch isZero kann man mit einer Funktion lösen, Ansätze sind hier beschrieben:
https://www.mql5.com/en/forum/156466
Es kann sein, dass mit NormalizeDouble() auch hin und wieder ein Abgleich ohne sowas funktioniert, ich würde mich aber nicht 100% darauf verlassen denn NormalizeDouble() liefert immer double zurück und das unterliegt nun mal immer der nicht präzisen Darstellung. Ich würde daher zwei doubles nicht ohne solche isEqual Funktionen vergleichen.

Letztendlich ist es wichtig, sich bewusst zu sein, dass Gleitkommazahlen in der Programmierung eine begrenzte Genauigkeit aufweisen und dass dies bei der Verarbeitung und Verwendung von Zahlen Berücksichtigung finden muss.
Was wir als Mensch wollen sind eigentlich Ergebnisse wie bei der dezimalen Handrechnung also die dezimale Festkommaarithmetik aber die ganzen Programmiersprachen bieten, zumindest nicht ohne Weiteres, leider kein solches Festkommaformat.

traderdoc 09.06.23 00:17

Hallo Cas$Digger,
danke für Deine Ausführungen.

traderdoc


Alle Zeitangaben in WEZ +2. Es ist jetzt 17:14 Uhr.

Powered by vBulletin® Version 3.8.5 (Deutsch)
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO 3.6.1
Powered by vBCMS® 2.7.0 ©2002 - 2024 vbdesigns.de
Copyright ©2009 - 2023 by Expert-Advisor.com - Das Metatrader Forum