java ungenauigkeit < Algorithmen < Schule < Informatik < Vorhilfe
|
Hallo !
Ich war kurz vorm austicken als ich festgestellt hab, warum mein programm nicht funktioniert...
Um zu zeigen, voran es lag, hab ich das problem mal vereinfacht rausgegriffen:
1: | public class tester1 {
| 2: |
| 3: | private double right;
| 4: | private double left;
| 5: |
| 6: | public tester1 () {
| 7: | }
| 8: |
| 9: | public void testen () {
| 10: |
| 11: | left = -0.1;
| 12: | right = -0.08;
| 13: |
| 14: | while (right < 0.1) {
| 15: |
| 16: | right = left+0.02;
| 17: |
| 18: | System.out.println("left:" + left + " " + "right:" + right);
| 19: |
| 20: | left = left + 0.02;
| 21: | }
| 22: | }
| 23: | } |
es wird bei mir ausgegeben:
left:-0.1 right:-0.08
left:-0.08 right:-0.06
left:-0.06 right:-0.039999999999999994
left:-0.039999999999999994 right:-0.019999999999999993
left:-0.019999999999999993 right:6.938893903907228E-18
left:6.938893903907228E-18 right:0.020000000000000007
left:0.020000000000000007 right:0.04000000000000001
left:0.04000000000000001 right:0.06000000000000001
left:0.06000000000000001 right:0.08000000000000002
left:0.08000000000000002 right:0.10000000000000002
das darf doch wohl nicht wahr sein !!!!!!!!
warum verfälschen sich die werte auf einmal. eigentlich sollte
left:-0.1 right:-0.08
left:-0.08 right:-0.06
left:-0.06 right:-0.04
left:-0.04 right:-0.02
left:-0.02 right:0
usw. ausgegeben werden...
Bitte helft mir und erklärt mir, woher dieser irsinn kommt und was ich dagegen machen kann.
|
|
|
|
Status: |
(Antwort) fertig | Datum: | 21:59 So 22.10.2006 | Autor: | piet.t |
Hallo Richard,
das Problem dabei ist, dass die Gleitkommavariablen intern ja binär dargestellt werden - und 0,02 in Binärdarstellung ist leider ein unendlicher "Binärbruch" ("Dezimalbruch" passt hier ja nicht....).
Etwas genauer: in Bnärdarstellung ist 0,02: 0,000001010001111.... und irgendwann wird's periodisch. Der Computer nimmt zwar ein paar mehr Nachkommastellen als ich, aber nichtsdestotrotz muss er irgendwann einmal abbrechen und dann entstehen Rundungsfehler. Nimmt man meine Beispieldarstellung und wandelt sie wieder in eine Dezimalzahl um, dann kommt eben nur noch 0,019989013671875 raus.
Wie kann man das vermeiden?
1. Möglichkeit: die Ergebnisse zwischendurch immer mal wieder auf eine vernünftige Genauigkeit runden, wobei ich nicht weiss, ob java da was vernünfitges anzubieten hat....
2. Möglichkeit: die Klasse BigInteger im Paket java.math stellt eine Dezimalzahl beliebiger Genauigkeit dar, d.h. da verschwinden dann auch die Rundungsfehler aus der Umwandlung ins Binärformat. Allerdings wird das Handling dann etwas umständlicher und das Programm wird schätzungsweise auch um ein paar Größenordnungen langsamer (was in diesem Beispiel aber noch nicht wirklich spürbar ist). Hier mal mein Beispiel:
1: | public class Tester {
| 2: |
| 3: |
| 4: | public Tester() {
| 5: | }
| 6: |
| 7: | public static void main(String[] args) {
| 8: | BigDecimal right;
| 9: | BigDecimal left;
| 10: | final BigDecimal step = new BigDecimal("0.02");
| 11: | final BigDecimal limit = new BigDecimal("0.1");
| 12: |
| 13: | left = new BigDecimal("-0.1");
| 14: | right = new BigDecimal("-0.08");
| 15: |
| 16: | while (right.compareTo(limit)< 0) {
| 17: |
| 18: | right = left.add(step);
| 19: |
| 20: | System.out.println("left:" + left + " " + "right:" + right);
| 21: |
| 22: | left = left.add(step);
| 23: | }
| 24: | }
| 25: | }
|
Damit sieht die Ausgabe wohl so aus, wie Du das erwartest. Wenn Du zu dem Beispiel noch Fragen hast, dann melde Dich einfach nochmal.
Gruß
piet
|
|
|
|
|
Hallo Piet !
Vielen Dank für deine ausführliche Antwort !
Aber leider wird bei mir wenn ich dein programm nehme immer "cannot find symbol -class BigDecimal" angezeigt.
ich hab erst vor 3 Tagen mit java angefangen, deshalb bin ich mir nicht so sicher, aber ganz oben muss doch "import java.lang.Math" hin, oder?? trotzdem klappts aber leider nicht ;)
am tollsten wäre es für mich natürlich wenn du mir zeigen könntest wie ich BigDecimal in meinem Programm integrieren kann ;)
Gruss Richard
|
|
|
|
|
Hallo,
das Problem ist nur der Ort, an dem man diese Klasse findet. Die richtige Importanweisug muss lauten:
import java.math.BigDecimal;
Dann sollte alles wieder funktionieren.
Unter Java-API kannst du im linken unteren Frame jede Klasse finden und in der Beschreibung dazu auch, wo du sie findest.
Gruß
Martin
|
|
|
|
|
Status: |
(Mitteilung) Reaktion unnötig | Datum: | 18:03 Mo 23.10.2006 | Autor: | piet.t |
Hoppla, das import java.math.BigDecimal; habe ich beim Kopieren doch glatt verpasst....Sorry! Aber Martin hat das ja schon geklärt!
Gruß
piet
|
|
|
|
|
Ok super ich danke euch beiden !
Grad noch eine letzte schnelle Frage, da ich mich für die rundmethode entschieden hab ;)
kennt jemand einen befehl, mit dem man die anzahl an nachkommastellen eines doubles rausfinden kann?
und wenn ja, wie findet man solche befehle ?
|
|
|
|
|
Status: |
(Antwort) fertig | Datum: | 19:37 Mi 25.10.2006 | Autor: | piet.t |
Hallo,
>
> kennt jemand einen befehl, mit dem man die anzahl an
> nachkommastellen eines doubles rausfinden kann?
Auf Anhieb wüsste ich da jetzt nichts, es kommt dann ja auch wieder darauf an, ob man Dezimal- oder Binärdarstellung meint, ob Nullen an letzter Stelle mitgezählt werden sollen (die haben ja immerhin auch Aussagekraft),.... Ausserdem reden wir hier ja von Gleitpunktzahlen und wie viele Nachkommastellen hat [mm] 1,375*10^{-20}???
[/mm]
Ich denke Du willst ja eigentlich auf eine feste Zahl von Dezimalstellen runden - da wäre hier ein Beispiel, wie man sowas in java anfangen kann.
> und wenn ja, wie findet man solche befehle ?
>
Befehle gibt es in java ja nicht allzu viele (es gibt meines Wissens weniger als 20 reservierte Schlüsselworte in der eigentlichen Programmiersprache), aber eine riesige Klassenbibliothek, die Methoden für alles mögliche bereitstellt (System.out.println() ist ja auch kein Befehl sondern eine Methode einer java-Standardklasse).
Bleibt die Frage, wie findet man die passende Mehtode zum Problem. Und da ist mein Weg üblicherweise langes, mehr oder weniger systematisches Stöbern in der
API-Dokumentation der java-Standardklassen.
In unserem Beispiel könnte man foglendermaßen überlegen: Runden hat möglicherweise was mit Mathe zu tun, also wären Kandidaten das Paket java.math oder die Klasse java.lang.Math (man beachte den Unterschied). Ausserdem könnte es noch etwas mit den Datentypen float oder double zu tun haben - hier empfiehlt sich dann ein Blick auf die entsprechenden Wrapper-Klassen (die heißen so, weil sie den elementaren Datentyp "einwickeln") java.lang.Float oder java.lang.Double.
Das ist am Anfang fürchterlich mühsam, aber mit der Zeit kriegt man einen Überblick über die wichtigsten Klassen und Pakete und gewint ein Gefühl dafür, wo man mit der Suche Anfangen könnte.
Gruß
piet
|
|
|
|
|
Status: |
(Mitteilung) Reaktion unnötig | Datum: | 23:23 So 29.10.2006 | Autor: | Bit2_Gosu |
vielen dank Piet !!!
Habs jetzt mal so gelöst:
1: | private int getNachkommastellen(final double x) {
| 2: |
| 3: | if (x==Math.round(x)) {
| 4: | return(0);
| 5: | }
| 6: |
| 7: | else {
| 8: | return((""+x).length()-(""+((int)x)).length()-1);
| 9: | }
| 10: | } |
|
|
|
|