Java Übungsaufgabe Java, "<=" scheint als "<" gelesen zu werden

ianos

Cadet 2nd Year
🎅 Nikolaus-Rätsel-Elite
Registriert
Mai 2013
Beiträge
17
Hallo zusammen,

kann mir jemand erklären, warum dieses Übungsprogramm (s.u.), bei seiner Ausführung bei x = 1.3 aufhört? Für mich sieht es aus, als würde "x <= xEnde" wie "x < xEnde" gelesen, da sonst ja x = 1.4 in der Ausgabe enthalten sein müsste? Die Ausgabe stoppt aber bei x = 1.3. (s.u.)
Offenkundig muss der Fehler vor dem Bildschirm sitzen, ich finde ihn aber einfach nicht. Für eine Erklärung meines Fehlers wäre ich sehr dankbar!

LG

Programm:

class Abschlussaufgabe{
public static void main(String[] args) {
double x = 1.0;
double xAnfang = 1.0;
double xEnde = 1.4;
double step = 0.1;
for (x = xAnfang; x <= xEnde; x += step) {
double y = 4 * (x * x) + 5 * x - 3;
System.out.println("x "+ "\t" + "y");
System.out.println(x + "\t" + y);
}
}
}

Ausgabe:

x y
1.0 6.0
x y
1.1 7.34
x y
1.2000000000000002 8.760000000000002
x y
1.3000000000000003 10.260000000000005
 
Ich vermute das liegt an der ungenauigkeit der Floatingpoint-Zahlen. Setz doch mal xEnde auf 1.5 und guck, was für ein Wert bei dem Durchlauf tatsächlich gespeichert ist.
 
1.3000000000000003 + 0.1 = 1.4000000000000003 > 1.4
 
  • Gefällt mir
Reaktionen: BeBur, McTheRipper und Thaddelino
Ah, das erklärt die Sache natürlich. Aber woher kommen die 0,000000000000002 bzw ..03? Step ist ja eigentlich als 0.1 deklariert?
xEnde = 1.5 hatte ich schon probiert, mich aber dann natürlich gewundert, dass bei x = 1.4 Schluss war..

Edit: Danke für den Link!

Edit 2: Damit ist meine Frage dann geklärt, nochmal danke an alle!
Wen es interessiert: Die Übungsaufgabe stammt aus dem Buch Vorkurs Informatik, von Müller, Weichert, 5. Aufl.
 
Zuletzt bearbeitet:
Die Lösung, dass es an der Ungenauigkeit liegt, wurde ja schon genannt :)

@ianos Was Du machen kannst, ist letztlich Genauigkeit tauschen gegen Rechengeschwindigkeit: Nimm statt double einfach BigDecimal, dann hast sollte Deine Schleife korrekt und wie erwartet abbrechen.
 
Alternativ nicht mit einem double iterieren sondern einem int und dann innerhalb der Schleife mittels start und step und dem int index den aktuellen Wert berechnen (xAnfang + i * step). So braucht man auch keinen BigDecimal.
 
  • Gefällt mir
Reaktionen: BeBur
ianos schrieb:
Ah, das erklärt die Sache natürlich. Aber woher kommen die 0,000000000000002 bzw ..03? Step ist ja eigentlich als 0.1 deklariert?

Gleitkommazahlen sind prinzipiell ungenau, und eine der Ungenauigkeiten im Umgang mit Dezimalzahlen ist mit ihrer Basis begründet: Dezimalzahlen (das sagt schon das Wort) haben die Basis 10, Binärzahlen haben die Basis 2. Da double und float zu Letzterem gehören, können sie nur Zahlen exakt aufnehmen, deren Binärdarstellung endlich und auf eine bestimmte Anzahl von Binärziffern (bei double höchstens 53, bei float höchstens 24) begrenzt ist.

Die Zahl 0.2 zur Basis 10 hat aber zur Basis 2 eine unendliche periodische Ziffernfolge, deswegen können weder float noch double diese Zahl verlustfrei aufnehmen.

Wie bereits geschrieben wurde nimmst du am besten BigDecimal.
 
Ach, funktionieren muss es ja gar nicht; ich wollte es nur verstehen.
Wenn ich double ersetze gibt Eclipse mir "BigDecimal cannot be resolved to a type" zurück. Aber vielleicht mache ich es auch falsch.. :D
 
Import nicht vergessen.

Import Java.math.BigDecimal;

BigDecimal xAnfang = new BigDecimal("1.0");

oder so in der Art sollte es gehen.
 
Was das bedeutet, weiß ich nicht. Das ist aber vermutlich vorerst in Ordnung, das gepostete Programm war für mich schon Turnen am Hochreck. Ich les dann erst mal weiter und mache einen Schritt nach dem anderen. 🙃
 
  • Gefällt mir
Reaktionen: NJay
Das bedeutet, dass du deinem Programm erst sagen musst, wo es die Klasse BigDecimal findet, da sie nicht durch dich definiert oder im Standard enthalten ist. Aber das wirst du später schon selber herausfinden :)

Generell zum Merken: float/double niemals auf Gleichheit testen. Hierfür schreibt man sich am besten eine eigene Funktion welche mit dem sog. Maschinenepsilon (jene Ungenauigkeit) als Abweichung vergleicht. Also ob 3.00000003 +- 0.00000005(Epsilon) == 3.00000000 ist.

Viel Spaß beim Lernen.
 
Vielfache von 0.1 können grundsätzlich nicht in floats ohne Genauigkeits Verlust gespeichert werden, was im Wesentlichen daran liegt dass es für 0.1 keine endliche binäre Repräsentation gibt, vergleichbar mit 1/3 = 0,333... im Dezimalsystem.
Es gibt noch mehr Einschränkungen bei floats generell verwendet man die nur wenn einem ca. Werte reichen.
 
ianos schrieb:
Ach, funktionieren muss es ja gar nicht; ich wollte es nur verstehen.

Im Wesentlichen kannste Binärzahlen, also die interne Repräsentation in Bits, nicht einfach so in jede Kommazahl umrechnen, das klappt einfach nicht. So als versuchtest du einen Bruch nach Kommazahl umzurechnen. 1/3 zb. Bei deinen Werten kommt halt 1.3000000001 raus wenn man Binär nach Dezimal konvertiert.

Unheimlich anspruchsvolle Lösung deines Problems: für eine Nachkommastelle alles *10, für 2 Nachkommastellen *100 und Ergebnis am Ende durch 10 oder 100 dividieren.
 
Guter Link, wobei "passieren" der falsche Ausdruck ist, da dies eben inhärent zu Fließkommaarithmetik ist und eben das in jeder Programmiersprache erwartete Ergebnis ist, wenn ein Datentyp "float" genannt wird.
 
Zurück
Oben