Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Additionsproblem
Dusty - Sa 02.03.13 14:28
Titel: Additionsproblem
Es scheint Zahlen zu geben, mit denen Delphi ein Problem mit dem rechnen hat.
Das ist mir unerklärlich, aber evt. kann mir hier jemand auf die Sprünge helfen.
Wenn ich 100,29 + 19,06 rechne, kommt 119,35 raus.
Wenn Delphi das rechnet, kommt offenbar etwas anderes dabei heraus?
Ein Formular mit Button und Editfeld reicht um das Problem klar zu machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm1.Button1Click(Sender: TObject); var d1, d2, d3, d4 : double; begin d1 := StrToFloat('100,29'); d2 := StrToFloat('019,06'); d3 := StrToFloat('119,35'); edit1.text := FloatToStr(d1+d2-d3); end; |
In meinem Editfeld steht jetzt: 1,06581410364015E-14 obwohl ich Null erwarten würde.
2. Kusiosum:
Ein Formular mit einem Button und einem Memofeld.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TForm1.Button2Click(Sender: TObject); var d : double; i : integer; begin Memo1.Lines.Clear; d := 0; For i := 1 to 100 do begin Memo1.Lines.Add(FloatToStr(d)); d := d + 0.001; end; end; |
Irgendwann tauchen im Memo1 diese Zahlensprünge auf:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| 0,082 0,083 0,084 0,085 0,086 0,087 0,0880000000000001 0,0890000000000001 0,0900000000000001 0,0910000000000001 0,0920000000000001 |
Wo kommt das her? Wer kann mir auf die Sprünge helfen??
Delphi5 Enterprise / Windows XP
Moderiert von
Narses: Delphi-Tags hinzugefügtModeriert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Mo 04.03.2013 um 09:26
Mathematiker - Sa 02.03.13 14:44
Hallo,
double, real, extended ... sind alles Datentypen, bei denen nicht die Ziffern sondern eine Näherungsdarstellung(!) gepeichert werden.
double ist auf 14 Stellen theoretisch genau. Durch die Summierung kommt es aber eher zu einer Abweichung. Nimmst Du extended statt double geschieht dies entsprechend später.
Bei Deinem 1.Beispiel entspricht die Abweichung gerade der Genauigkeit von double.
Übrigens ist dies kein spezielles Problem von Delphi. Diese Abweichungen treten bei allen, derartigen Programmiersprachen auf.
Beste Grüße
Mathematiker
mandras - Sa 02.03.13 14:44
Das Problem ist schön erläutert unter:
http://de.wikipedia.org/wiki/Gleitkommazahl
insbes. unter Überschrift "Dezimalzahlen", wo darauf hingewiesen wird,
daß schon Zahlen wie 0.1 nur näherungsweise darstellbar sind.
Dusty - Sa 02.03.13 14:54
Okay, also ganz normal ... keine fehlerhaften Einstellungen oder sowas.
Danke für eure Hilfe!
Dusty - Sa 02.03.13 15:18
Was mich am meisten irritiert hatte, war, dass der Debugger immer GLEICHE Zahlen angezeigt hat.
Der Debugger ist Schuld ;-)
Tranx - Sa 02.03.13 15:56
Diese "Ungenauigkeit" ist auch der Grund dafür, dass mit solchen Zahlen oder Ergebnissen von Rechnungen keine Vergleiche mit 0 Sinn machen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| a := 0.01; b := 0.0212; c := 0.0312; d := c - b; if a-d=0.0 then tuewas;
if abs(a-d)<1e-13 then tuewas; |
Dusty - Sa 02.03.13 18:42
Ja, so ähnlich habe ich es dann auch gemacht.
Allerdings reicht mir ein > 0.001 :-)
Wie gesagt, im Debugger wurden die GLEICHEN Zahlen angezeigt und das machte die Fehlersuche beschwerlich ;-)
Die Methode "Abs(x-y) > 0.001" ist natürlich die richtige Lösung!
Hm - größer oder kleiner... ?! Kleiner für den Vergleich auf Gleichheit ... ok
Größer um den potientiellen Rundungsfehler von 1 Cent aufzuspüren ;-)
Dusty - Sa 02.03.13 19:08
Aber noch nicht in Delphi 5 :-)
Tranx - Sa 02.03.13 21:44
Das ist kein Problem, kann man doch nachbilden:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100:
| function SameValue(const A, B: Extended; Epsilon: Extended): Boolean; overload; function SameValue(const A, B: Double; Epsilon: Double): Boolean; overload; function SameValue(const A, B: Single; Epsilon: Single): Boolean; overload;
function NotDef(const a : Extended) : boolean; overload; function NotDef(const a : Double) : boolean; overload; function NotDef(const a : Single) : boolean; overload;
function NotDef(a: Extended): Boolean; var b: extended; begin try if a = 0.0 then Result := False else begin b := a * 1.0001; if (b = a) then Result := True else Result := False; end; except Result := True; end; end;
function NotDef(a: Double): Boolean; var b: extended; begin try if a = 0.0 then Result := False else begin b := a * 1.0001; if (b = a) then Result := True else Result := False; end; except Result := True; end; end;
function NotDef(a: Single): Boolean; var b: extended; begin try if a = 0.0 then Result := False else begin b := a * 1.0001; if (b = a) then Result := True else Result := False; end; except Result := True; end; end;
function SameValue(const A, B: Extended; Epsilon: Extended): Boolean; begin if NotDef(a) or NotDef(b) then Result := False else Result := (Abs(A - B) < Epsilon); end;
function SameValue(const A, B: Extended; Epsilon: Extended): Boolean; begin if NotDef(a) or NotDef(b) then Result := False else Result := (Abs(A - B) < Epsilon); end;
function SameValue(const A, B: Double; Epsilon: Double): Boolean; begin if NotDef(a) or NotDef(b) then Result := False else Result := (Abs(A - B) < Epsilon); end;
function SameValue(const A, B: Single; Epsilon: Single): Boolean; begin if NotDef(a) or NotDef(b) then Result := False else Result := (Abs(A - B) < Epsilon); end; |
Anmerkung: Fehler bei Übergabe von 0 an die Funktion NotDef abgefangen! Manchmal ist man sowas von der Rolle. O*irgendwas ergibt ja immer 0 und damit würde 0 eine nicht definierte Zahl ergeben, aber dem ist ja nicht so. Also wird vorher auf 0 abgefragt.
Fie Funktion NotDef übergibt bei Zahl = +/-INF TRUE ansonsten FALSE. (Test auf definirte bzw. nicht definierte Zahl).
ich habe mir gesagt, und das hat was für sich, dass eine nicht definierte Zahl (+/-INF) ja nicht unbedingt gleich einer anderen nicht definierten Zahl ist, daher das Ergebnis FALSE, falls eine oder beide Zahlen nicht definiert sind. Interessanterweise macht der Rechner eine Multiplikation mit 1.0001 bei einer nicht definierten Zahl Es ergibt sich aber in dem Fall die gleiche nicht definierte Zahl. So kann man definierte Zahlen von nicht definierten unterscheiden.
Die Funktion Samevalue arbeitet auch mit nicht definierten Zahlen, ist getestet.
Dusty - So 03.03.13 20:22
Klasse :-)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!