# double (un-)genauigkeit



## javalerner86 (8. Jul 2012)

Hallo zusammen,

das problem der ungenauigkeit bei der berechnung von gleitkommazahlen ist vielen bestimmt bekannt (Ursache: aus umrechnung der sprachen in bits usw.).

*Gibt es eigentlich eine Möglichkeit dieses "Problem" zu umgehen oder zu beheben? *

ich rede nicht von geldbeträgen zusammenrechnen, wo man einfach sagen kann: multiplizier mal hundert und nimm dafür den typ integer. Ich rede z.B. von Messwerten, die unterschiedlich viele Ziffern hinter dem komma haben können. 
Wäre schade, wenn ein Messergebnis z.B. um paar Millimeter von der Literatur abweicht und das sonst so schöne Ergebnis "versauen" würde. Vielen Dank.


----------



## kaschik (8. Jul 2012)

BigDecimal: Galileo Computing :: Java ist auch eine Insel – 12.6 Große Zahlen *


----------



## javalerner86 (8. Jul 2012)

Danke, ok, dass hatte ich auch gefunden. Aber kann man aus dieser dann eine Wurzel ziehen? dies habe ich nicht gefunden=(

Math.sqrt(Decimal Zahl);

sqrt funktioniert leider nur mit double oder float Zahlen, oder?


----------



## Marco13 (8. Jul 2012)

Beschreib' vielleicht nochmal genauer, worum es geht. So pauschal kann man da kaum mehr sagen als "BigDecimal", aber das ist schon "schweres Geschütz" (eine kompliziertere Rechnung darauf umzustellen ist ein Krampf, und birgt auch einiges an Bug-Potential....).


----------



## HoaX (8. Jul 2012)

javalerner86 hat gesagt.:


> Wäre schade, wenn ein Messergebnis z.B. um paar Millimeter von der Literatur abweicht


Wie wird denn (was) gemessen? In der Regel ist die Messmethode ungenauer als double selbst, von dem her kann man auch meistens den entsprechenden Rundungsfehler getrost ignorieren.


----------



## AquaBall (8. Jul 2012)

javalerner86 hat gesagt.:


> das problem der ungenauigkeit bei der berechnung von gleitkommazahlen ist vielen bestimmt bekannt
> *Gibt es eigentlich eine Möglichkeit dieses "Problem" zu umgehen oder zu beheben? *



Ganz losgelöst von Computer:
Das Problem kannst du auch im echten Leben nie lösen!
Diese "Ungenauigkeit" begleitet dich auf jedem Zettel und überall in der Methemetik.

Schon "primitive" Zahlen wie 1/3 kannst du NIE im Leben exakt schreiben.
1/3 = 0.333 333 333 333 333 ... Irgendwann ist der Zettel zu Ende. Irgendwann musst du den Rest als Ungenauigkeit akzeptieren.
Bei "schwierigren" Zahlen wie Wurzel(2) = 1,41421356... ist noch viel früher Schluss.

Die Mathematik behält sich ihre Genauigkeit nur dadurch, dass sie symbolische Schreibweisen erfunden hat (Peridodenpunkt), oder überhaupt symbolisch bleibt (Pi, Wurzel, e ...).
und das auch nur, solange es "Mathematik" bleibt, und kein "Rechnen" wird.

Es ist aber eine Irrmeinung, dass der Computer deshalb ungenauer rechnen muss als der Mensch.
Tatsache ist, dass der Computer im Binärsystem nur Brüche von 2 exakt darstellen kann, 
Und das dem Menschen im Dezimal System nur mit den Faktoren 2 und 5 gelingt.
Jede andere Zahl MUSS ungenau bleiben.

Deshalb wurden auch genügend Systeme entwickelt, in denen du am Computer symbolisch EXAKT rechnen kannst.


----------



## kaschik (8. Jul 2012)

@AquaBall: Dass man numerisch nicht exakt rechnen kann, sollte jedem klar sein und wird hier auch von niemandem verlangt! Der TO spricht ausdrücklich von Messwerten und die können selbst schon nicht exakt sein. Aber wenn ein Messwert mit einer bestimmten Genauigkeit vorliegt, dann sollte diese Genauigkeit auch beibehalten werden.

@javalerner86: Wenn du die Wurzel ziehen willst, dann geht das mit Math.sqrt nicht mehr, aber dir stehen genug Alternativen offen:
- du schreibst dir selbst eine sqrt-Methode, die mit BigDecimal arbeitet. Algorithmen siehe Wikipedia: Methods of computing square roots - Wikipedia, the free encyclopedia
- du nutzt Libraries, die solche Methoden bereitstellen. Zum Beispiel JScience bietet eine Decimal-Klasse an, mit der man auf eine bestimmte Genauigkeit Wurzeln ziehen kann: JScience


----------



## javalerner86 (8. Jul 2012)

@ alle vier Beiträge erstmal vielen Dank für die Hilfe, obwohl das "Problem" nicht so richtig gelöst ist, sorry

Ich möchte gemessene Werte mit einem Programm mit Formel berechnen (welche formel ist ja eigentlich egal, begründung jetzt). Ein einfaches Beispiel: 1.0-0.8 ergibt eigentlich 2.0, aber das Programm gibt auf Grund der Umrechnung der Zahlen in ein Binärsystem nicht die richtig exakte Zahl aus, sondern 1.99999999996 oder so.
Ich bin mir darüber im klaren, dass nicht unendlich viele Ziffen hinter dem Komma angezeigt werden können, aber das eben gezeigte Beispiel ist erschreckend (@Aquaball). Für soetwas suche ich eine Lösung. Diese könnte ich dann allgemein übertragen.

@kaschik: danke, ich werde mir die Klasse JScience ansehen


----------



## HoaX (8. Jul 2012)

Stell dir die Frage ob es für den Benutzer relevant ist ob das Ergebnis 2.00000001 oder 2.0000 ist  --> Zeig nur eine bestimmte Anzahl von Nachkommastellen an, dann wird aus der 1.999999999999 auch wieder eine 2.0000


```
System.out.printf("%.4f", 1.9999999999999999999d);
```


----------



## javalerner86 (8. Jul 2012)

@Hoax
Naja wenn das ergebnis nicht für weitere berechnungen verwendet werden würde, dann stimme ich dir zu. nur müsste ich mit einer matrix berechnung (und so weiter) die exakte Zahl haben. und genau die das Problem habe ich nun. Wenn ein Eintrag genau 0 werden soll, aber trotzdem irgendwas kleines herauskommt, stimmt die weitere berechnung nicht mehr.
Falls es was bringt: ich hab versucht einen Algorithmus zur Berechnung von Gleichungssystemen zu programmieren. Klappt auch prima, jedoch ist es schade, wenn schon bei einfachen Zahlen der Unterschied zu exakten ergebnissen erheblich ist, als wenn man von hand rechnet. Dann brauch ich mir keine gedanken zu machen, wenn ich statt einfachen zahlen dann messwerte als zahlen einlesen lasse. Das ist die eigentliche idee dahinter.
Vllt gibt es doch eine lösung dazu.


----------



## Marco13 (8. Jul 2012)

Lass' dich von sowas wie 1.0/9.0=0.111111111111111111113243 erstmal nicht zu sehr irritieren. 

Bei "komplexeren" numerischen Problemen, oder etwa einem Gleichungssystemlöser, muss man IMHO zwei vollkommen unterschiedliche Fälle betrachten: 

Erstens, dass die Zahlen (schon in einfachen Fällen) nicht "schön aussehen": Wenn man den Benutzer eine 2x2-Matrix eingeben läßt und mit einem Vektor multipliziert, und dann die Ausgabe mit Standard-Formatierung eben 1.0000000000000001231 ist, kann man (wenn es wirklich nur darum geht) die Formatierung ändern, damit 1.0 angezeigt wird. 

Der zweite Fall ist, dass die Ungenauigkeit sich wirklich fortpflanzt und auf-akkumuliert, und am Ende ein numerisch vollkommen falsches Ergebnis rauskommt. Das ist dann aber ein Problem, das in der Praxis auch nicht mit BigDecimal gelöst werden kann. (Einen Gleichungssystemlöser für 1000x1000-Matrizen auf BigDecimal umstellen würde keinen Spaß machen, und über die Ausführungzeit im Vergleich zu double bräuchten wir dann gar nicht zu reden). In solchen Fällen muss man vermutlich tiefer ansetzen: Andere (numerisch stabilere) Lösungsverfahren suchen, bessere Preconditioner verwenden oder was auch immer...


----------



## javalerner86 (8. Jul 2012)

@Marco13:
Danke für deinen Beitrag. Genau dein zweiter Fall trifft trifft auf mein Problem zu. Ich benutze vier oder fünf matrixmultiplikationen und der fehler pflanzt sich so fort, dass sich mein ergebnis von 2.6 auf 0.2 ändert, was ein fataler fehler ist (ein Fehler aber 4 oder 5 stelle hinter dem komma, könnte ich --auch wenn das schon ein wenig ärgerlich ist-- verschmerzen). Daher hatte ich gehofft, ich hätte wirklich im i-net eine einfache lösung dafür übersehen (auch wenn ich hier vllt getadelt worden wäre, dass ich nicht weitergesucht hätte).
Wenn jemand eine tolle lösung hat, die das Problem löst, BITTE posten. Nach der Suche bei google musste ich feststellen, dass ich nicht der einzige mit dem problem bin.


----------



## kaschik (8. Jul 2012)

Solange du im Bereich der rationalen Zahlen bleibst, also keine Wurzeln ziehst o. Ä., kannst du dir doch auch eine Bruch-Klasse schreiben. Für die Multiplikation von Matrixen und z. B. Grauß-Jordan eignet sich das hervorragend.


----------



## HoaX (8. Jul 2012)

javalerner86 hat gesagt.:


> dass sich mein ergebnis von 2.6 auf 0.2 ändert


Kannst du die Matrizen mal zeigen? Ich kann mir das nicht wirklich vorstellen dass Double so einen Fehler produziert. Sicher dass der Fehler nicht doch wo anders steckt?


----------



## Kevin94 (8. Jul 2012)

Ich weis zwar nicht ob das was hilft, weil ich's selber noch nie gebraucht habe, aber in Java gibts doch auch den 
	
	
	
	





```
strictfp
```
 Zusatz für Methoden, wenn diese absolut sauber mit Gleitkommazahlen rechnen sollen. Zusätzlich zu Math gibts auch die Klasse StrictMath, die sich von Math nur darin unterscheidet, dass alle Methoden diesen Zusatz tragen.
Wenn das das Problem nicht lösen sollte, halte ich es auch für eine gute Idee wenn du dir eine Bruchklasse schreibst und dann mit Ganzzahlen rechnest und am Ende kürzt.


----------



## protip (8. Jul 2012)

Kevin94 hat gesagt.:


> Ich weis zwar nicht ob das was hilft, weil ich's selber noch nie gebraucht habe, aber in Java gibts doch auch den
> 
> 
> 
> ...


Toller tipp, dadurch werden die Rechnungen doch höchstens _noch_ ungenauer...


----------



## Kevin94 (9. Jul 2012)

Einen Versuch ist es trotzdem Wert, bevor man Stunden darin investiert ein fast fertiges Programm komplett umzustellen (Meine Meinung).


----------



## reNur (9. Jul 2012)

Du musst halt einfach vorher den maximalen Rundungsfehler deines Algorithmus ausrechnen, und dann die Genaugikeit so lange erhöhen, bis der Rundungsfehler sozusagen im Toleranzbereich ist.


----------



## HoaX (9. Jul 2012)

Kevin94 hat gesagt.:


> Einen Versuch ist es trotzdem Wert, bevor man Stunden darin investiert ein fast fertiges Programm komplett umzustellen (Meine Meinung).



Ist es nicht! Wenn man sich durchliest was strictfp mach, dann ist es auch klar warum. Warum sollte es einen versuch Wert sein u.U. noch ungenauer zu rechenen und zu hoffen genauere Ergebnisse zu bekommen?


----------



## mla.rue (9. Jul 2012)

kA wie groß jetzt deine Messwerte sind (ich hatt ein ähnliches Problem mit Kundenabrechnung, das hatte halt 2 Nachkommastellen). Multipliziere deinen Messwert mit 100 (oder mehr/weniger) und konvertiere es so in einen int, dann addieren, und dann wieder durch 100 teilen


----------



## faetzminator (9. Jul 2012)

mla.rue hat gesagt.:


> Multipliziere deinen Messwert mit 100 [...]


Schön, kannst du lesen 


javalerner86 hat gesagt.:


> ich rede nicht von geldbeträgen zusammenrechnen, wo man einfach sagen kann: multiplizier mal hundert [...]


----------

