Java-Schleifen: int versus double

Gerade habe ich eine Anfrage zu meiner Übungsaufgabe aus meinem Java-Grundkurs erhalten. Ich habe in der Lösung eine for-Schleife mit einer int-Variable vorgeschlagen. Warum nicht eine double-Variable als Schleifenvariable verwenden?

Problematisch bei double-Schleifen sind die Rundungsfehler. Betrachten Sie z.B.:

class Test {
  public static void main(String[] args) {
    for(double x=0; x<=2; x+=0.1)
      System.out.println(x);
  }
}

Man würde hier annehmen, dass die Schleife 21 mal durchlaufen wird und dabei die Werte 0, 0.1, 0.2, …, 2.0 ausgegeben werden. Tatsächlich lautet die Ausgabe:

0.0
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
1.0999999999999999
1.2
1.3
1.4000000000000001
1.5000000000000002
1.6000000000000003
1.7000000000000004
1.8000000000000005
1.9000000000000006

Das Problem ist dabei nicht, dass einzelne Zahlen ungenau erscheinen — das ist ein Darstellungsproblem, alle Werte sind gleich ‚ungenau‘ bzw. so genau, wie es eben geht. Schlimm ist aber, dass die Schleife nur 20 Mal durchlaufen wird. Wenn Sie damit ein Vieleck zeichnen wollen, dann fehlt im Vieleck der letzte Strich.

Dieses Problem ist systemimmanent und nicht zu vermeiden. Fließkommazahlen in der üblichen IEEE-64- oder IEEE-80-Bit-Darstellung sind mit Rundungsfehlern behaftet, das ist nicht zu ändern, auch nicht durch die Wahl einer anderen Programmiersprache.

Das Problem lässt sich umgehen, in dem beim Vergleich mit dem Endwert (hier 2.0) diesen mit einem kleinen Delta vergrößert, also die Schleife so formuliert:

for(double x=0; x<=2.000001; x+=0.1)

Dann werden bei diesem Beispiel auch bei Rundungsfehlern garantiert immer alle beabsichtigten Werte durchlaufen.

Nur: man vergisst dieses Delta oft oder berechnet es falsch. Viele double-Schleifen funktionieren ja ohnedies wie beabsichtigt, oft arbeitet der Rundungsfehler ja zum eigenen Gunsten. Nur verlassen kann man sich eben nicht darauf.

Und aus diesem Grund ziehe ich, wenn es irgendwie möglich ist, Integer-Variablen für Schleifen vor und führe danach gegebenenfalls eine Umrechnung in eine Fließkommazahl durch. Also:

class Test {
  public static void main(String[] args) {
    for(int i=0; i<=20; i++) {
      double x =  (double)i / 10.0;
      System.out.println(x);
    }
  }
}