Unklarheit bei einer for-Schleife
-
Hallo,
obwohl ich schon einige Zeit C++ programmiere, bin ich heute zum ersten Mal auf folgenden Fehler gestossen:
int main() { for(double i = -2; i <= 2; i += 0.2) { cout << i << endl; } return 0; }
Der Code sollte eigenlich nur i von -2 bis +2 in Schritten von 0.2 inkrementieren lassen. Soweit klar allerdings bekomme ich folgenden Output:
-2 -1.8 -1.6 -1.4 -1.2 -1 -0.8 -0.6 -0.4 -0.2 -2.77556e-16 0.2 0.4 0.6 0.8 1 1.2 1.4 1.6 1.8 2
Warum steht bei 0 nicht 0? Wenn ich in 0.5er Schritten inkrementiere, passt alles einwandfrei und er zeigt 0.
Sorry aber verstehe das gerade gar nicht
Ich verwende den g++ (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609
Kann mir da jemand helfen?
Thx & Lg
-
Dein Computer arbeitet (wie fast alle Computer) im BinĂ€rsystem. Im BinĂ€rsystem ist 0.2 nicht exakt mit endlich vielen Stellen darstellbar (ungefĂ€hr so, wie man 1/3 nicht exakt im Dezimalsystem ausschreiben kann). Wenn du 0.2 schreibst, rechnet der Computer in Wirklichkeit mit so etwas wie 0.20000000000000011102230246252, was der nĂ€chstbeste Wert fĂŒr ihn ist. Aber 5 Mal diese Zahl ist eben nicht exakt 1, sondern ein bisschen mehr. Daher kommt deine for-Schleife nicht exakt bei 0 vorbei.
0.5 ist hingegen exakt im BinÀrsystem darstellbar, da geht es gut.
Jetzt fragst du vielleicht, wieso deine Zwischenwerte exakt zu stimmen scheinen. Das liegt an der Anzeigegenauigkeit. Die liegt standardmĂ€Ăig bei 7 Stellen hinter der ersten Ziffer, die nicht 0 ist. Wenn er also bei -1.8 ist, dann ist er zwar in Wirklichkeit bei -1.79999999999999988897769753748, aber der wenn man das auf 7 Stellen rundet, dann kommt 1.8 heraus. Wenn er bei 0 ist, ist er hingegen bei 0.00000[ganz viele Nullen]000277556. Und wenn man das auf sieben relevante Stellen ausgibt, dann erhĂ€lt man das, was du bekommst.
Mögliche Abhilfen hĂ€ngen davon ab, was du erreichen möchtest: Wenn es dir um die Anzeige geht, dann lĂ€sst sich an den Anzeigeeinstellungen schrauben. Wenn es dir darum geht, dass die Zahlen exakt sind, dann musst du anders rechnen, beispielsweise mit BrĂŒchen.
Beachte auch, dass Schleifen dieser Art eventuell nicht so oft laufen, wie man denkt. Hier war der interne Wert von 0.2 ein kleines bisschen gröĂer als 0.2. Wenn du eine Schrittweite gewĂ€hlt hĂ€ttest, die intern etwas kleiner als erwartet wĂ€re, dann wĂ€re die Schleife einmal hĂ€ufiger gelaufen.
-
Hi!
danke fĂŒr deine ausfĂŒhrliche Antwort, das hat mir jetzt geholfen!
Lg
Buzz
-
Das Verhalten rĂŒhrt von Datentyp her. In anderen Sprachen wie z.B. C# kommt es zum gleichen Ergebnis. - Ich nehme mal an, dass hier die Datentyp-Genauigkeit eine Rolle spielt.
Bei float sieht das Ergebnis noch mehr Kraut und RĂŒben aus.
-2
-1.8
-1.6
-1.4
-1.2
-0.99999998
-0.79999998
-0.59999998
-0.39999999
-0.19999999
1.490116E-07
0.20000002
0.40000002
0.60000001
0.80000001
1
1.2
1.4
1.6
1.8
-
ErgÀnzend zu dem was SeppJ schon gesagt hat, hier noch eine mögliche Lösung, falls du einfach nur sicherstellen willst, dass 0 auch tatsÀchlich exakt 0 ist:
#include <iostream> int main() { for( int i = -10; i <= 10; ++i ) std::cout << i * 0.2 << '\n'; }
EDIT: Diese Lösung hat ferner auch den Vorteil, dass die Schleife immer exakt 21 mal lÀuft, unabhÀngig davon ob der Offset ein kleines bisschen zu gross oder zu klein ist.
-
Ich weià gar nicht mehr, wo/von wem ich es gehört habe, aber:
Wenn du ein Problem hast (z.B. Schleife in 0.2er-Schitten durchlaufen) und versuchst es mit Floating-Point zu lösen, hast du auf einmal 2.000001 Probleme