Discussion:
Addieren von zwei double
(zu alt für eine Antwort)
Patrick
2005-04-12 07:55:14 UTC
Permalink
hallo NG

Momentan verstehe ich java gerade gar nicht mehr:

double d = 0.01;
double dd = 0.2;
System.out.println(d+dd);

ergibt 0.21000000000000002

wieso das und wie kann ich sicherstellen das korrekt gerechnet wird?

Besten Dank
Stefan Matthias Aust
2005-04-12 08:02:01 UTC
Permalink
Post by Patrick
double d = 0.01;
double dd = 0.2;
System.out.println(d+dd);
ergibt 0.21000000000000002
wieso das und wie kann ich sicherstellen das korrekt gerechnet wird?
Es wird korrekt innerhalb der Rechengenauigkeit der Zahlen gerechnet.
Das ist ein ganz alter Hut. float und double sind *inexakte* Datentypen,
die aufgrund der internen Zahlenrepräsentation viele im 10er System
harmlos aussehende Zahlen nicht exakt repräsentieren können.

Wenn du's genau haben willst, nimm *exakte* Datentypen, also
Integerzahlen, Brüche oder binär kodierte Dezimalzahlen oder so was
ähnliches. Leider bietet Java dafür nicht viel Unterstützung und "dank"
fehlender Möglichkeit, +, -, usw. zu überladen, werden Berechnungen auch
leicht häßlich.
--
Stefan Matthias Aust
Ingo R. Homann
2005-04-12 08:47:52 UTC
Permalink
Hi,
Post by Patrick
hallo NG
double d = 0.01;
double dd = 0.2;
System.out.println(d+dd);
ergibt 0.21000000000000002
wieso das und wie kann ich sicherstellen das korrekt gerechnet wird?
Besten Dank
doubles haben halt - wie dezimal dargestellte Zahlen auch - eine
beschränkte Genauigkeit: So, wie Du 1/3 nicht exakt als Dezimalzahl
darstellen kannst, kannst Du 1/10 eben nicht exakt als (binär codierten)
double darstellen.

java.math.BigDecimal könnte dir helfen.

Ciao,
Ingo
Frank Buss
2005-04-12 09:17:52 UTC
Permalink
Post by Ingo R. Homann
doubles haben halt - wie dezimal dargestellte Zahlen auch - eine
beschränkte Genauigkeit: So, wie Du 1/3 nicht exakt als Dezimalzahl
darstellen kannst, kannst Du 1/10 eben nicht exakt als (binär codierten)
double darstellen.
Genau genommen kann man jede rationale Zahl als Dezimalzahl darstellen:

http://de.wikipedia.org/wiki/Dezimalzahl

1/3 ist dann 0.3, wobei über der 3 ein Strich ist. Nur die meisten
irrationalen Zahlen kann man nicht als endliche Dezimalzahl darstellen.

Für 1/3 hilft übrigens auch BigDecimal nicht weiter, da es Brüche nicht
exakt abbilden kann, wie es besser durchdachte Sprachen, wie Lisp oder
Smalltalk, ohne extra Aufwand können.
--
Frank Buß, ***@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
Sönke Müller-Lund
2005-04-12 11:08:12 UTC
Permalink
Post by Frank Buss
Für 1/3 hilft übrigens auch BigDecimal nicht weiter, da es Brüche nicht
exakt abbilden kann, wie es besser durchdachte Sprachen, wie Lisp oder
Smalltalk, ohne extra Aufwand können.
Was hat das mit der Sprache zu tun?
Ich würde eine Klasse Rational nicht unbedingt als "extra Aufwand"
bezeichnen?

Sönke
Stefan Matthias Aust
2005-04-12 12:37:35 UTC
Permalink
Post by Sönke Müller-Lund
Was hat das mit der Sprache zu tun?
Ich würde eine Klasse Rational nicht unbedingt als "extra Aufwand"
bezeichnen?
Wenn du sie selbst schreiben musst (wie in Java) und dann auch noch
"r1.add(r2)" schreiben musst statt r1 + r2 (wie in Java), dann ist das
garantiert beides ein Extraaufwand im Gegensatz zu Smalltalk, was
einfach mit Brüchen rechnen kann, da eine Klasse "Fraction" dort schon
eingebaut ist.
--
Stefan Matthias Aust
Ingo R. Homann
2005-04-12 14:13:23 UTC
Permalink
Hi,
Post by Stefan Matthias Aust
Post by Sönke Müller-Lund
Was hat das mit der Sprache zu tun?
Ich würde eine Klasse Rational nicht unbedingt als "extra Aufwand"
bezeichnen?
Wenn du sie selbst schreiben musst (wie in Java) und dann auch noch
"r1.add(r2)" schreiben musst statt r1 + r2 (wie in Java), dann ist das
garantiert beides ein Extraaufwand im Gegensatz zu Smalltalk, was
einfach mit Brüchen rechnen kann, da eine Klasse "Fraction" dort schon
eingebaut ist.
ACK.

Allerdings stellen sich mir dabei einige Fragen:

Muss man in Smalltalk den Typ der Variablen deklarieren, oder wird das
automatisch gemacht? Welchen Typ hat "1" (int?), "0.1" (decimal?),
"0.25" (binary double?), "1/3" (fract?)?

Was passiert, wenn man z.B. "decimal 0.1" und "fract 1/3" addiert? Geht
das? Welchen Typ hat das Ergebnis?

Kann man Variablen (für Performance-kritische Sachen) so deklarieren,
dass sie immer als "double" repräsentiert werden (*), auch auf die
Gefahr hin, dass man dann mit den Rundungsfehlern leben muss?

Ciao,
Ingo

(*) Ich unterstelle, dass "natives" Rechnen mit double schneller ist als
Rechnen mit unterschiedlichen Typen + Konvertierungen + Typ-Checks
Stefan Matthias Aust
2005-04-12 15:18:16 UTC
Permalink
Post by Ingo R. Homann
Muss man in Smalltalk den Typ der Variablen deklarieren
Nein. Das ist unmöglich.
Post by Ingo R. Homann
oder wird das automatisch gemacht?
Ja.
Post by Ingo R. Homann
Welchen Typ hat "1" (int?)
SmallInteger
Post by Ingo R. Homann
"0.1" (decimal?),
"0.25" (binary double?)
Beides ist Float. VisualWorks kennt "0.1s" für ScaledDecimal, so meine
ich. Mit "0.25d" würde man ein Double anlegen.
Post by Ingo R. Homann
"1/3" (fract?)?
Fraction. Zu beachten ist aber, dass das kein Literal ist, sondern wir
sehen hier "/" als Factory Methode, die aus zwei Integern ein
Fraction-Objekt macht. "1/3*3" wäre demzufolge 1 (ein SmallInteger).
Post by Ingo R. Homann
Was passiert, wenn man z.B. "decimal 0.1" und "fract 1/3" addiert? Geht
das? Welchen Typ hat das Ergebnis?
Frage, was passiert, wenn man exakte Zahlen addiert: Hier wird zwischen
Fraction und Integer (das ist die gemeinsame Oberklasse von SmallInteger
und LargeInteger die keine Obergrenze haben, als BigInteger in Java
entsprechen) hin- und herkonvertiert wie man es vermutet.

Ist ein Objekt ein inexakter Typ, wird das Ergebnis ebenfalls inexakt,
meist ein Double. Was bei ScaledDecimal und Fraction passiert, weiss
ich nicht.
Post by Ingo R. Homann
Kann man Variablen (für Performance-kritische Sachen) so deklarieren,
dass sie immer als "double" repräsentiert werden (*), auch auf die
Nein. Man deklariert in Smalltalk keine Variablentypen. Ein Objekt ist
ein double oder ist es nicht. Das hat nichts mit der Variablen zu tun.
Post by Ingo R. Homann
Gefahr hin, dass man dann mit den Rundungsfehlern leben muss?
Wenn du doubles willst, schreib das hin.

12 asDouble / 7

oder

12.0d / 7.0d

(12d kann man nicht schreiben, denn das würde die Nachricht #d an den
Integer 12 schicken).

Es ist eher so, dass man manchmal auf Fraction verzichten möchte und
lieber bei Integer-Objekten bleiben möchte. Dafür gibt es eben "/", was
ggf. automatisch Brüche erzeugt, noch eine Extramethode: //. Ein

(4 / 3) asInteger

würde zwar das selbe geben wie "4 // 3", aber erstmal einen Bruch bauen,
um den dann wieder umzuwandeln.
Post by Ingo R. Homann
(*) Ich unterstelle, dass "natives" Rechnen mit double schneller ist als
Rechnen mit unterschiedlichen Typen + Konvertierungen + Typ-Checks
Garantiert. Existierende Smalltalk-Systeme sind aber nicht gut mit
doubles (64 bit), da sie diese immer boxen müssen. Daher ist bei
VisualWorks der Standardtyp Float, was eine 30-bit-Fließkommazahl ist.

Man kann sich damit behelfen, Vektorbefehle mit DoubleArrays zu
definieren und seine Algorithmen entsprechend anzupassen. Das spart das
ewige boxen.
--
Stefan Matthias Aust
Ingo R. Homann
2005-04-13 06:50:11 UTC
Permalink
Hi SMA,
Post by Stefan Matthias Aust
"0.1" (decimal?), "0.25" (binary double?)
Beides ist Float.
Float im "allgemeinen" Sinne ("binär codiert")? D.h. "0.1" wäre auch
dort nicht gleich "0.1"?

D.h. wenn ich exakt "0.1" haben will, muss ich 1/10 schreiben. (So, wie
ich auch in Java und den meisten anderen Sprachen nicht exakte 0.1 habe,
wenn ich 0.1 schreibe.)
Post by Stefan Matthias Aust
"1/3" (fract?)?
Fraction. Zu beachten ist aber, dass das kein Literal ist, sondern wir
sehen hier "/" als Factory Methode, die aus zwei Integern ein
Fraction-Objekt macht.
Das ist ein interessanter Ansatz.
Post by Stefan Matthias Aust
"1/3*3" wäre demzufolge 1 (ein SmallInteger).
Wie ist denn die *-Methode implementiert? Rechnet sie erst das Ergebnis
als fraction aus, kürzt und erkennt, dass das Ergebnis als int
darstellbar ist?

Was passiert dann z.B. bei

1.0 / 3 * 3.0

Wenn ich deine weiteren Ausführungen richtig verstehe, wäre das Ergebnis
ein float 1.0?

Ciao,
IRH
Stefan Matthias Aust
2005-04-13 07:14:46 UTC
Permalink
Post by Ingo R. Homann
Post by Stefan Matthias Aust
Beides ist Float.
Float im "allgemeinen" Sinne ("binär codiert")? D.h. "0.1" wäre auch
dort nicht gleich "0.1"?
Korrekt.
Post by Ingo R. Homann
Post by Stefan Matthias Aust
"1/3*3" wäre demzufolge 1 (ein SmallInteger).
Wie ist denn die *-Methode implementiert? Rechnet sie erst das Ergebnis
als fraction aus, kürzt und erkennt, dass das Ergebnis als int
darstellbar ist?
Exakt.
Post by Ingo R. Homann
Was passiert dann z.B. bei
1.0 / 3 * 3.0
Wenn ich deine weiteren Ausführungen richtig verstehe, wäre das Ergebnis
ein float 1.0?
Ja. Bereits 1.0 / 3 ist ein Float
--
Stefan Matthias Aust
Ulrich Schramme
2005-04-12 09:30:19 UTC
Permalink
Post by Patrick
hallo NG
double d = 0.01;
double dd = 0.2;
System.out.println(d+dd);
ergibt 0.21000000000000002
wieso das und wie kann ich sicherstellen das korrekt gerechnet wird?
Besten Dank
Den Effekt kannst Du auch in anderen Programmiersprachen bewundern. Die
interne Binärdarstellung bringt solche Fehler mit sich. Von daher sollte
man double bzw. float Werte auch nicht auf Gleichheit testen.

Ulli
--
Software & IT Service Schramme: www.sits-schramme.de
privat: www.u-schramme.de
Bernd Eckenfels
2005-04-12 20:09:00 UTC
Permalink
Post by Patrick
wieso das und wie kann ich sicherstellen das korrekt gerechnet wird?
runden oder BigDecimal verwenden. Das ist uebrigens kein java problem.

Gruss
Bernd
Loading...