Discussion:
Newbie-Problem beim Subtrahieren von Double-Variablen
(zu alt für eine Antwort)
R. Ernst
2004-10-12 17:40:37 UTC
Permalink
Hallo NG!

Ein wahrscheinlich triviales Problem, ich komme aber trotzdem nicht auf die
Loesung...

ich moechte mit einem programm eine muenzausgabe simulieren. natuerlich
so,dass mir moeglichst wenig muenzen ausgegeben werden.
realisiert habe ich das indem ich den auszugebenden betrag in einer double
variablen speicher, von dem dann die jeweils ausgegeben muenze abziehe und
so weiter.

leider tritt, je nach ausgangsbetrag ein "rundungsfehler" (mein problem)
auf, so dass ich nie auf einen restbetrag von 0.0 komme. woran liegt das?

vorweg: das ist tag zwei mit java fuer mich ; )

anbei der source des programms:

class Muenzausgabe {
public static void main (String[] args) {
double betrag;
double muenze;
betrag = Double.parseDouble (args[0]);
int i = 0;

System.out.println ("Betrag: " + betrag);

while (betrag > 0)
{
System.out.print (i++);
if (betrag >= 2.0) { betrag = betrag - 2.0; muenze = 2.0; }
else if (betrag >= 1.0) { betrag = betrag - 1.0; muenze = 1.0; }
else if (betrag >= 0.5) { betrag = betrag - 0.5; muenze = 0.5; }
else if (betrag >= 0.2) { betrag = betrag - 0.2; muenze = 0.2; }
else if (betrag >= 0.1) { betrag = betrag - 0.1; muenze = 0.1; }
else if (betrag >= 0.05) { betrag = betrag - 0.05; muenze = 0.05; }
else if (betrag >= 0.02) { betrag = betrag - 0.02; muenze = 0.02; }
else { betrag = betrag - 0.01; muenze = 0.01; }
System.out.println ("Verwendete Muenze: " + muenze + " Restbetrag: " +
betrag);
}
}
}
Stefan Matthias Aust
2004-10-12 14:46:30 UTC
Permalink
Post by R. Ernst
leider tritt, je nach ausgangsbetrag ein "rundungsfehler" (mein problem)
auf, so dass ich nie auf einen restbetrag von 0.0 komme. woran liegt das?
Das liegt daran, wie jeder Computer typischerweise Fließkommazahlen
darstellt - hat gar nichts mit Java zu tun. Nimm Ganzzahlen, mit denen
du Pfennigbeträge repräsentierst.

Allgemein gilt: Wenn es exakt sein soll, verzichte auf double oder
float. Dies sind inexakte Datentypen, die nur eine Näherung an die
exakten Werte bieten und viele Werte gar nicht darstellen können (1/3
etwa).
--
Stefan Matthias Aust
Tor-Einar Jarnbjo
2004-10-12 23:38:30 UTC
Permalink
Post by Stefan Matthias Aust
Das liegt daran, wie jeder Computer typischerweise Fließkommazahlen
darstellt - hat gar nichts mit Java zu tun. Nimm Ganzzahlen, mit
denen du Pfennigbeträge repräsentierst.
Das hat schon was mit Java zu tun. Auch wenn der Computer ganz korrekt mit
Fließkommazahlen rechnen könnte, würde ein darauf laufendes Java-Programm
"falsch" rechnen, wenn die VM die Sprachspezifikation richtig umgesetzt
hat.

Gruß, Tor
Paul Ebermann
2004-10-13 17:53:03 UTC
Permalink
Post by Tor-Einar Jarnbjo
Post by Stefan Matthias Aust
Das liegt daran, wie jeder Computer typischerweise Fließkommazahlen
darstellt - hat gar nichts mit Java zu tun. Nimm Ganzzahlen, mit
denen du Pfennigbeträge repräsentierst.
Das hat schon was mit Java zu tun. Auch wenn der Computer ganz korrekt mit
Fließkommazahlen rechnen könnte, würde ein darauf laufendes Java-Programm
"falsch" rechnen, wenn die VM die Sprachspezifikation richtig umgesetzt
hat.
Zunächst:

Das heißt nicht "Fließkomma" sondern "Gleitkomma".
(fließen = to flow, gleiten = to float).

Was heißt generell "korrekt mit Gleitkommazahlen rechnen"?
"Immer die richtige Genauigkeit haben"?

Das widerspricht ein wenig dem Prinzip der Gleitkommazahl,
dass nämlich nur begrenzt viele Nachkommastellen vorhanden
sind, und die in einer bestimmten Basis dargestellt werden.


Paul
--
Die Homepage von de.comp.lang.java: http://www.dclj.de
Pauls Package-Sammlung: http://purl.org/NET/ePaul/#pps
Tor-Einar Jarnbjo
2004-10-13 19:35:23 UTC
Permalink
Post by Paul Ebermann
Das heißt nicht "Fließkomma" sondern "Gleitkomma".
(fließen = to flow, gleiten = to float).
Entschuldigung Herr Ebermann, aber ich bin Ausländer. Anscheinend war es
doch aber klar, was ich gemeint habe, oder?
Post by Paul Ebermann
Was heißt generell "korrekt mit Gleitkommazahlen rechnen"?
"Immer die richtige Genauigkeit haben"?
Tja, z.B., dass 1./5. genau als 0,2 ausgewertet wird.
Post by Paul Ebermann
Das widerspricht ein wenig dem Prinzip der Gleitkommazahl,
dass nämlich nur begrenzt viele Nachkommastellen vorhanden
sind, und die in einer bestimmten Basis dargestellt werden.
Naja, schon. Aber der Grund dafür, dass dieser Ausdruck in Java falsch ist,
hat höchstens indirekt damit zu tun wie "Computer typischerweise
Fließkommazahlen darstellt":

.01+.01+.01+.01+.01+.01==.06

Die Auswertung ist in der Sprachspezifikation festgeklopft und würde auch
auf exotischer Hardware, die intern mit Basis 10 arbeitet ebenso falsch
sein.

Gruß, Tor
Alexander Schueller
2004-10-14 14:36:21 UTC
Permalink
Post by Tor-Einar Jarnbjo
Naja, schon. Aber der Grund dafür, dass dieser Ausdruck in Java falsch ist,
hat höchstens indirekt damit zu tun wie "Computer typischerweise
.01+.01+.01+.01+.01+.01==.06
Die Auswertung ist in der Sprachspezifikation festgeklopft und würde auch
auf exotischer Hardware, die intern mit Basis 10 arbeitet ebenso falsch
sein.
Nun, ich würde dies nicht so herum begründen, sondern genau andersherum.
WEIL die gängige Hardware eben Gleit- oder Fließkommazahlen SO unterstützt
(übrigens genormt), hat man dies in Java so spezifiziert...
und nicht um Newbies zu ärgern :-)

Beste Grüße
Alex
Paul Ebermann
2004-10-14 12:05:36 UTC
Permalink
Das hei t nicht "Fliekomma" sondern "Gleitkomma".
(flie en = to flow, gleiten = to float).
Entschuldigung Herr Ebermann, aber ich bin Auslnder. Anscheinend war es
doch aber klar, was ich gemeint habe, oder?
Ja.

Entschuldige, sollte nicht gegen dich gehen.
Ist ja ein häufiger Fehler - allein in diesem
Thread haben es auch SMA und Alexander Schueller
falsch gemacht (die anderen haben das Wort nicht
benutzt). Ich habe das nur hier angehängt,
weil ich auf dein Posting sowieso geantwortet habe
(ich habe ja auch den Fehler von SMA zitiert).

Hätte ich freundlicher formulieren können.
Was hei t generell "korrekt mit Gleitkommazahlen rechnen"?
"Immer die richtige Genauigkeit haben"?
Tja, z.B., dass 1./5. genau als 0,2 ausgewertet wird.
Also "Dezimal rechnen"?
Das widerspricht ein wenig dem Prinzip der Gleitkommazahl,
dass nmlich nur begrenzt viele Nachkommastellen vorhanden
sind, und die in einer bestimmten Basis dargestellt werden.
Naja, schon. Aber der Grund daf r, dass dieser Ausdruck in Java falsch ist,
hat hchstens indirekt damit zu tun wie "Computer typischerweise
.01+.01+.01+.01+.01+.01==.06
Er hat etwas damit zu tun, dass die Dezimaldarstellung
nicht wirklich für die "computertypischen Gleitkommazahlen"
(in Binärdarstellung) geeignet ist.
Die Auswertung ist in der Sprachspezifikation festgeklopft und wrde auch
auf exotischer Hardware, die intern mit Basis 10 arbeitet ebenso falsch
sein.
Ja.

Aber auch Software, die diese exotische Hardware ausnutzen würde,
würde bei 1./3 Probleme bekommen.

Es gibt für jede Basis rationale Zahlen, die sich nicht
mit endlich vielen Stellen exakt darstellen lassen.


Paul
--
Die dclj-FAQ wird immer freitags hier gepostet.
Eine (nicht ganz aktuelle) HTML-Version gibt es hier:
http://de.geocities.com/uweplonus/faq/
Alexander Schueller
2004-10-12 14:52:10 UTC
Permalink
Hallo Raphael!
Post by R. Ernst
ich moechte mit einem programm eine muenzausgabe simulieren. natuerlich
so,dass mir moeglichst wenig muenzen ausgegeben werden.
realisiert habe ich das indem ich den auszugebenden betrag in einer double
variablen speicher, von dem dann die jeweils ausgegeben muenze abziehe und
so weiter.
Warum möchtest Du double, also Fließkomma nehmen?
Post by R. Ernst
leider tritt, je nach ausgangsbetrag ein "rundungsfehler" (mein problem)
auf, so dass ich nie auf einen restbetrag von 0.0 komme. woran liegt das?
Das liegt an der Natur der Fließkommazahlen. Es läßt sich nicht jede
Zahl exakt als Fließkommazahl darstellen! Auch nicht, wenn Du mit mehr
Genauigkeit (also double) arbeitest. Solltest Du bei Gelegenheit
mal nach dem Thema googeln.

Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int? Dann hast Du auch keine Rundungsprobleme!

Natürlich mußt Du dann statt 1 100 abziehen :)
Post by R. Ernst
vorweg: das ist tag zwei mit java fuer mich ; )
Hat nicht wirklich was mit Java zu tun.

Beste Grüße
Alex
R. Ernst
2004-10-12 18:09:08 UTC
Permalink
Post by Alexander Schueller
Warum möchtest Du double, also Fließkomma nehmen?
hatte es zuerst mit float probiert, da ist leider das gleiche problem
aufgetaucht
Post by Alexander Schueller
Das liegt an der Natur der Fließkommazahlen. Es läßt sich nicht jede
Zahl exakt als Fließkommazahl darstellen!
schon klar
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch moeglich sein,
in solchen bereichen genau zu rechnen.

trotzdem danke

raphael
Alexander Schueller
2004-10-12 15:20:52 UTC
Permalink
Post by Alexander Schueller
Warum möchtest Du double, also Fließkomma nehmen?
hatte es zuerst mit float probiert, da ist leider das gleiche problem aufgetaucht
float ist ebenfalls ein Fließkommazahlentyp, jedoch mit geringerer
Genauigkeit als double.
Post by Alexander Schueller
Das liegt an der Natur der Fließkommazahlen. Es läßt sich nicht jede
Zahl exakt als Fließkommazahl darstellen!
schon klar
Deshalb kannst Du weder mit float, noch mit double, noch mit einer
Fließkommazahl höherer Genauigkeit an dieser Stelle sauber arbeiten.
Der einzige "Workaround" wäre, nicht exakt auf Gleichheit mit 0.0
zu prüfen, sondern einen ausreichend großen Abstand zu 0.0 zu
definieren, für den Du 0 ansetzt. Du prüfst dann, ob Dein
Rest innerhalb dieses Abstands zu 0.00 ist. Deine Schleifenbedingung
könnte z.B. so aussehen:


while (betrag > 0.001)


Achtung: Da Du Dich von "oben" aus dem positiven Bereich an 0.0
herantastest, reicht diese Bedingung. Andernfalls natürlich:

while (betrag < -0.001 || betrag > 0.001)
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch moeglich sein, in solchen bereichen genau zu rechnen.
Dies ist KEIN Workaround, sondern eine saubere Lösung. Du willst exakt
rechnen. Dafür brauchst Du einen exakt rechnenden Datentypen. Eben
z.B. int. Einen Workaround habe ich Dir oben angegeben!

Ansonsten kannst Du auch exakte Fließkommatypen implementieren, wie sie
z.B. auch in Programmiersprachen wie COBOL existieren oder bei
relationalen Datenbanken (NUMERIC/DECIMAL). Aber das wäre für Deinen
Algorithmus nicht nötig.
trotzdem danke
raphael
Gerne
Beste Grüße
Alex
Alexander Schueller
2004-10-12 15:32:21 UTC
Permalink
Post by Alexander Schueller
Ansonsten kannst Du auch exakte Fließkommatypen implementieren, wie sie
z.B. auch in Programmiersprachen wie COBOL existieren oder bei
relationalen Datenbanken (NUMERIC/DECIMAL). Aber das wäre für Deinen
Algorithmus nicht nötig.
BigDecimal lag mir natürlich auf der Zunge...

:-)

Haben andere ja auch bereits gepostet.

Beste Grüße
Alex
Michael Borgwardt
2004-10-12 15:24:09 UTC
Permalink
Post by R. Ernst
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch moeglich sein,
in solchen bereichen genau zu rechnen.
Ist doch genau. Halbe cents gibt es nicht, und auch bei professionellen
Bankanwendungen wird gerundet (mit genauen Regeln wie das zu geschehen hat).

Aber schau Dir mal die Klasse java.util.BigDecimal an, die ist genau dafür da.
Michael Borgwardt
2004-10-12 15:25:10 UTC
Permalink
Post by Michael Borgwardt
Aber schau Dir mal die Klasse java.util.BigDecimal an, die ist genau dafür da.
java.math.BigDecimal ist natürlich gemeint.
R. Ernst
2004-10-12 18:31:38 UTC
Permalink
Post by Michael Borgwardt
Post by Michael Borgwardt
Aber schau Dir mal die Klasse java.util.BigDecimal an, die ist genau dafür da.
java.math.BigDecimal ist natürlich gemeint.
Stefan Ram
2004-10-12 15:25:01 UTC
Permalink
Post by R. Ernst
Post by Alexander Schueller
Das liegt an der Natur der Fließkommazahlen. Es läßt sich
nicht jede Zahl exakt als Fließkommazahl darstellen!
schon klar
Ist Dir z.B. der Wert von 1E20+1-1E20 auch klar?
Post by R. Ernst
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend
Fließkomma- zahlen. Warum rechnest Du nicht mit Cent, und
nimmst dafür einen ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch
moeglich sein, in solchen bereichen genau zu rechnen.
Es ist nicht mehr ein "workaround" als dies jede andere
Darstellung von Zahlen ist. Aber vielleicht gefällt Dir
folgende Klasse:

http://java.sun.com/j2se/1.5.0/docs/api/java/math/BigDecimal.html

Damit werden die erwähnten Abweichungen nicht auftreten.
Mathias Kalb
2004-10-12 15:29:49 UTC
Permalink
Post by R. Ernst
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch moeglich sein,
in solchen bereichen genau zu rechnen.
Warum empfindest du dies als Workaround? Gerade bei deinem Beispiel
ist das doch sinnvoll und naheliegend mit Cent zu rechnen.

Du kannst dir mal die Klasse java.math.BigDecimal anschauen.

Ciao
Mathias Kalb
karlheinz klingbeil
2004-10-12 15:46:02 UTC
Permalink
R. Ernst schrub am Dienstag, 12. Oktober 2004 20:09
folgendes:

Vorweg: Rundungsfehler sind bei Double/Float normal.
Post by R. Ernst
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht
zwingend Fließkomma- zahlen. Warum rechnest Du nicht
mit Cent, und nimmst dafür einen ganzzahligen
Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss
doch moeglich sein, in solchen bereichen genau zu
rechnen.
Nun, in deinen Augen ist es ein Workaround, in anderer
Leutz Augen ist es seit 40 Jahren Standard, dass man
für Berechnungen mit Geldbeträgen passende Datentypen
verwendet, die keine Rundungsfehler entstehen lassen.
Bei Java nimmt man da z.B. BigDecimal, oder für
einfache Programme kann man auch den vorgeschlagenen
Trick mit integer-Werten und Cent-Beträgen anwenden.
--
greetz Karlheinz Klingbeil (lunqual)
http://www.lunqual.de oder http:www.lunqual.net
Achim Peters
2004-10-12 23:56:17 UTC
Permalink
Post by R. Ernst
Post by Alexander Schueller
Warum möchtest Du double, also Fließkomma nehmen?
hatte es zuerst mit float probiert, da ist leider das gleiche problem
aufgetaucht
Post by Alexander Schueller
Das liegt an der Natur der Fließkommazahlen. Es läßt sich nicht jede
Zahl exakt als Fließkommazahl darstellen!
schon klar
Post by Alexander Schueller
Für Dein Problem brauchst Du allerdings auch nicht zwingend Fließkomma-
zahlen. Warum rechnest Du nicht mit Cent, und nimmst dafür einen
ganzzahligen Datentypen wie int?
weil das in meinen augen ein workaround ist! es muss doch moeglich sein,
in solchen bereichen genau zu rechnen.
Das siehst Du IMHO völlig korrekt.

Lediglich genügen die (BTW auch in Sprachen ausserhalb Java von IEEE
genormten) Typen double und float nicht Deinen Anforderungen. Das ist
diesen beiden Typen aber prinzipbedingt inhärent - sie definieren eine
binäre Exponential-Darstellung mit endlicher Mantisse; eine dezimale 0,1
z. B. lässt sich aber binär nicht als endliche rationale Zahl und damit
durch float und double gar nicht präzise darstellen.

Von daher ist die dezimale Repräsentation gemäß IEEE-Norm für double und
float für bestimmte Zahlen immer fehlerbehaftet.

Es gibt, wie hier teilweise bereits erwähnt, diverse andere Ansätze.
Grundsätzlich ist das, wie von Dir richtig bemerkt, beliebig lösbar.
Schreibe notfalls Deine eigene akurate Klasse.

Bye
Achim
Loading...