Autor Beitrag
Dusty
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: 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:
ausblenden 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.
ausblenden 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:
ausblenden 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 user profile iconNarses: Delphi-Tags hinzugefügt
Moderiert von user profile iconNarses: Topic aus Sonstiges (Delphi) verschoben am Mo 04.03.2013 um 09:26
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Sa 02.03.13 14:40 
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1448

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 432
Erhaltene Danke: 107

Win 10
Delphi 6 Prof, Delphi 10.4 Prof
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: Sa 02.03.13 14:54 
Okay, also ganz normal ... keine fehlerhaften Einstellungen oder sowas.
Danke für eure Hilfe!
Dusty Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 648
Erhaltene Danke: 85

WIN 2000, WIN XP
D5 Prof
BeitragVerfasst: 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:


ausblenden 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 //wird u.U. nicht funktionieren
      tuewas;

    if abs(a-d)<1e-13 then //das funktioniert schon eher
      tuewas;

_________________
Toleranz ist eine Grundvoraussetzung für das Leben.
Dusty Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: Sa 02.03.13 18:57 
Dafür gibt es SameValue in Unit math.
Dusty Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: Sa 02.03.13 19:08 
Aber noch nicht in Delphi 5 :-)
Tranx
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 648
Erhaltene Danke: 85

WIN 2000, WIN XP
D5 Prof
BeitragVerfasst: Sa 02.03.13 21:44 
Das ist kein Problem, kann man doch nachbilden:

ausblenden volle Höhe 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.

_________________
Toleranz ist eine Grundvoraussetzung für das Leben.
Dusty Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 62

Windows XP
Delphi 5 Enterprise
BeitragVerfasst: So 03.03.13 20:22 
Klasse :-)