Autor |
Beitrag |
Dusty
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: Sa 02.03.13 14:28
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
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Sa 02.03.13 14:40
|
|
Mathematiker
      
Beiträge: 2622
Erhaltene Danke: 1448
Win 7, 8.1, 10
Delphi 5, 7, 10.1
|
Verfasst: 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
_________________ Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
|
|
mandras
      
Beiträge: 432
Erhaltene Danke: 107
Win 10
Delphi 6 Prof, Delphi 10.4 Prof
|
Verfasst: Sa 02.03.13 14:44
Das Problem ist schön erläutert unter:
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 
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: Sa 02.03.13 14:54
Okay, also ganz normal ... keine fehlerhaften Einstellungen oder sowas.
Danke für eure Hilfe!
|
|
Dusty 
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: 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
      
Beiträge: 648
Erhaltene Danke: 85
WIN 2000, WIN XP
D5 Prof
|
Verfasst: 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; |
_________________ Toleranz ist eine Grundvoraussetzung für das Leben.
|
|
Dusty 
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: 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 
|
|
WasWeißDennIch
      
Beiträge: 653
Erhaltene Danke: 160
|
Verfasst: Sa 02.03.13 18:57
Dafür gibt es SameValue in Unit math.
|
|
Dusty 
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: Sa 02.03.13 19:08
Aber noch nicht in Delphi 5 
|
|
Tranx
      
Beiträge: 648
Erhaltene Danke: 85
WIN 2000, WIN XP
D5 Prof
|
Verfasst: Sa 02.03.13 21:44
Das ist kein Problem, kann man doch nachbilden:
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.
_________________ Toleranz ist eine Grundvoraussetzung für das Leben.
|
|
Dusty 
      
Beiträge: 62
Windows XP
Delphi 5 Enterprise
|
Verfasst: So 03.03.13 20:22
Klasse 
|
|