Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Fließkommazahlen auf Gleichheit prüfen (https://www.delphipraxis.net/80640-fliesskommazahlen-auf-gleichheit-pruefen.html)

Luckie 12. Nov 2006 22:23


Fließkommazahlen auf Gleichheit prüfen
 
Jeder weiß, oder sollte wissen, dass man dies nicht tun sollte. Warum, das werde ich noch zeigen. Allerdings bräuchte ich mal ein kleines Delphi-Beispiel, um dies zu zeigen.

Folgendes geht leider:
Delphi-Quellcode:
var
  a: Double;
  b: Double;

begin
  a := 19.3;
  b := 2 * 9.65;

  if a = b then
    Writeln('gleich')
  else
    Writeln('ungleich');
  Readln;
Ich bräuchte aber ein entsprechende Beispiel, beim dem die Abfrage auf Gleichheit nicht funktioniert.

fwsp 12. Nov 2006 22:39

Re: Fließkommazahlen auf Gleichheit prüfen
 
bei mir ist folgendes ungleich:
Delphi-Quellcode:
var
  a, b: Real;

begin
  a := 12345789.987654321;
  b := 24691357.9975308642 / 2;

  if a = b then
    Writeln('gleich')
  else
    Writeln('ungleich');
  Readln;
end.
Edit1:
Nach der Delphi 7 PE Hilfe ist Double = Real.

Luckie 12. Nov 2006 22:47

Re: Fließkommazahlen auf Gleichheit prüfen
 
Das Beispiel macht aber nur Sinn, wenn man durch zwei anstatt durch fünf teilt. ;)

Danke.

Dan habe ich einfach zu wenige Stellen genommen beim Testen.

fwsp 12. Nov 2006 22:53

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von Luckie
Das Beispiel macht aber nur Sinn, wenn man durch zwei anstatt durch fünf teilt. ;)

ich kann mich nicht erinnern irgendwo was anderes geschrieben zu haben...
daniel muss wohl gemeiner weise meine post nach
Code:
/ 2
parsen lassen und es durch
Code:
/ 5
ersetzen lassen.

Dax 12. Nov 2006 23:16

Re: Fließkommazahlen auf Gleichheit prüfen
 
10.0/3.0 und 10*(1.0/3.0) tun genau das selbe bei weniger Schreibaufwand :)

Luckie 12. Nov 2006 23:52

Re: Fließkommazahlen auf Gleichheit prüfen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:

Zitat von fwsp
bei mir ist folgendes ungleich:
Delphi-Quellcode:
var
  a, b: Real;

begin
  a := 12345789.987654321;
  b := 24691357.9975308642 / 2;

  if a = b then
    Writeln('gleich')
  else
    Writeln('ungleich');
  Readln;
end.

Bei mir aber nicht:
Delphi-Quellcode:
var
  a, b: Double;

begin
  a := 12345789.987654321;
  b := 24691579.975308642 / 2;

  Writeln('a: ', a);
  Writeln('b: ', b);
  if a = b then
    Writeln('gleich')
  else
    Writeln('ungleich');
  Readln;
Ausgabe:
Code:
a: 1.23457899876543E+0007
b: 1.23457899876543E+0007
gleich
Du hast dich vertan. Zwei mal 12345789.987654321 ist nicht 24691357.9975308642 sondern 24691579.975308642.

@Dax auch dein Beispiel ergibt 'gleich'.

EDIT: Übrigens das Beispiel von dieser Seite: http://www.mpdvc.de/artikel/FloatingPoint.htm funktioniert in Delphi auch nicht, aber in C. Wo liegt der Unterschied bei der Zahlenbehandlung von Delphi und C?

Im Anhang mal mein Artikel als PDF zu der Prolematik.

Luckie 13. Nov 2006 12:31

Re: Fließkommazahlen auf Gleichheit prüfen
 
OK, es liegt der Schluss nahe, dass die Delphi Datentypen für dieses Beispiel von Haus aus schon zu genau sind. ;)

Cöster 13. Nov 2006 12:40

Re: Fließkommazahlen auf Gleichheit prüfen
 
wenn man nicht a (=3/10) und b (=3/10) vergleicht, sondern a (=3/10) und 3/10, ist es ungleich, vorrausgesetzt, dass a Double ist. Wenn man Extended wählt, muss man a und 10*(1/3) vergleichen, damit es ungleich ist.

Luckie 13. Nov 2006 12:48

Re: Fließkommazahlen auf Gleichheit prüfen
 
Ist der Artikel denn so verständlich und sachlich richtig oder gibt es noch Ergänzungen / Korrekturen?

fwsp 13. Nov 2006 12:50

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von Luckie
Ist der Artikel denn so verständlich und sachlich richtig oder gibt es noch Ergänzungen / Korrekturen?

sollte eigentlich soweit richtig sein.

SirThornberry 13. Nov 2006 13:02

Re: Fließkommazahlen auf Gleichheit prüfen
 
Ich denke es liegt eher daran das der Compiler optimiert und schon die richtigen Werte einsetzt.
Wenn du die Optimierung abschaltest und das ganze über Zwischenvariablen zuweist hast du eventuell mehr Glück.

Luckie 13. Nov 2006 13:04

Re: Fließkommazahlen auf Gleichheit prüfen
 
Die Optimierung hatte cih versuchsweise schon mal abgeschaltet, hat aber auch nichts genützt. Aber wiedem auch sein, ein Beispiel in C tut es auch. Man ist ja flexibel und ist in der Lage sich seine Programmiersprache der Aufgabe entsprechend auszusuchen. ;)

Luckie 13. Nov 2006 23:22

Re: Fließkommazahlen auf Gleichheit prüfen
 
Liste der Anhänge anzeigen (Anzahl: 1)
So, Artikel ist fertig:

Artikel: Prüfe Fließkommazahlen nie auf Gleichheit!

Abstract
Probleme beim Vergleichen von Fließkommazahlen. Es ist ein beliebter Anfängerfehler, auch wenn ihn hin und wieder auch mal Programmierer machen, die es wissen sollten, weil sie eigentlich über das nötige Hintergrundwissen verfügen (sollten). Und zwar der Vergleich von Fließkommazahlen auf Gleichheit. Was ist daran so gefährlich und warum funktioniert es eben nicht immer?

Link: Prüfe Fließkommazahlen nie auf Gleichheit!

Und im Anhang das endgültige PFD.

alzaimar 14. Nov 2006 07:18

Re: Fließkommazahlen auf Gleichheit prüfen
 
Luckie: So ist es eindrucksvoller, da der oft verwendete Vergleich mit 0 schon hier in die Hose geht.
Delphi-Quellcode:
Var
  a,b : Double;

begin
  a := 0.1;
  b := a - 0.1;
  if b=0 then
    ShowMessage('gleich')
  else
    ShowMessage ('ungleich');
end;
Der Grund ist klar: 0.1 lässt sich mit Floating Point nun mal nicht 100% genau darstellen, ergo ist 0.1f <> -(-0.1)f. Folgerichtig funktioniert aber das hier (weil typisierte Konstanten mit Variablen gleichzusetzen sind):
Delphi-Quellcode:
Const
  C : Double = 0.1;
Var
  a,b : Double;

begin
  a := C;
  b := a - C;
  if b=0 then
    ShowMessage('gleich')
  else
    ShowMessage ('ungleich');
end;
Aber das hier wiederum nicht :mrgreen:
Delphi-Quellcode:
Const
  C = 0.1;
Var
  a,b : Double;

begin
  a := C;
  b := a - C;
  if b=0 then
    ShowMessage('gleich')
  else
    ShowMessage ('ungleich');
end;
Weil Konstanten per definitionem nur Platzhalter sind.

Fazit: Man kann sich einfach nicht darauf verlassen, das "wo 0 draufsteht auch 0 drin ist".

[edit=sakura] BB-Code aktiviert. Mfg, sakura[/edit]


[edit] C in den Code eingefügt, danke Sir Thornberry[/edit]

SirThornberry 14. Nov 2006 11:27

Re: Fließkommazahlen auf Gleichheit prüfen
 
wo ist jetzt der unterschied? C wird in beiden Fällen definiert aber nirgends verwendet :gruebel:

alzaimar 14. Nov 2006 11:39

Re: Fließkommazahlen auf Gleichheit prüfen
 
:oops: und deswegen auch:
Delphi-Quellcode:
While Time < StrToTime ('09:30') Do
  Alzaimar.CanPostImForum := False

Sourcemaker 14. Nov 2006 11:44

Re: Fließkommazahlen auf Gleichheit prüfen
 
Hallo Luckie,

ich habe gerade Deinen Artikel gelesen und mir fehlt die Erwähnung des Typs Currency,
den benutze ich immer um entsprechende Vergleichsfehler auszuschließen.

Grüße

Frank

alzaimar 14. Nov 2006 11:50

Re: Fließkommazahlen auf Gleichheit prüfen
 
Currency hat 4 feste Nachkommastellen. Es handelt sich eigentlich um einen 64bit Integer Wert, also um (z.B.) 10000stel Cent.

Mit 4 Nachkommastellen kommt man aber auch nicht weit. Die richtige Vorgehensweise beim Rechnen mit reelen Zahlen ist entweder die Verwendung von Schutzstellen, oder die Verwendung von BCD-Datentypen. Diese sind aber nicht Bestandteil von Delphi.

Sourcemaker 14. Nov 2006 12:14

Re: Fließkommazahlen auf Gleichheit prüfen
 
alzaimar:
Zitat:

Currency hat 4 feste Nachkommastellen. Es handelt sich eigentlich um einen 64bit Integer Wert, also um (z.B.) 10000stel Cent.

Mit 4 Nachkommastellen kommt man aber auch nicht weit. Die richtige Vorgehensweise beim Rechnen mit reelen Zahlen ist entweder die Verwendung von Schutzstellen, oder die Verwendung von BCD-Datentypen. Diese sind aber nicht Bestandteil von Delphi.
Für die vielen mit Delphi realisierten kaufmännischen Anwendungen und viele andere, die nicht gerade astronomische Zahlen vorausetzen, ist es aber eine gute Lösung.

Grüße

Frank

alzaimar 14. Nov 2006 12:22

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von Sourcemaker
...
Für die vielen mit Delphi realisierten kaufmännischen Anwendungen und viele andere, die nicht gerade astronomische Zahlen vorausetzen, ist es aber eine gute Lösung....

Kaufmännische Anwendungen implizieren ja geradezu Currency, schließlich geht es ja ums Geld.

Eine Anwendung von uns berechnet Materialmengen, und da reicht Single z.B. schon gar nicht mehr aus. Die Berechnungen gehen über viele Iterationen und gerade hier potzenzieren sich Rundungsfehler. Wir arbeiten mit Double (das ist dann knapp ausreichend) und zeigen dann maximal 4 Nachkommastellen an. Gerade Iterationen benötigen verdammt viele Schutzstellen.

Die Anwendung (und auch die Zahlen) ist übrigens irdisch :zwinker: .

Eigentlich benötigen fast alle mathematischen Anwendungen, die sich nicht nur aufs Addieren beschränken, eine höhere Genauigkeit (Anzahl der Stellen), als sie Currency bietet.

Dafür ist Currency eben 100% genau. Bei Addition und Subtraktion. Und da sind 0.1 Euro - 0.1 Euro garantiert = 0,0000!

Luckie 14. Nov 2006 12:23

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von Sourcemaker
ich habe gerade Deinen Artikel gelesen und mir fehlt die Erwähnung des Typs Currency,

Wie schon gesagt, ist das nur ein getarnter Integer. Somit fällt die Verwendung des Datentyps Currency unter den Tipp Ganzzahlen zu verwenden. Zudem ist die Genauigkeit so gering, dass sich mathematische Berechnung, die eine höhere Genauigkeit erfordern, damit nicht durchführen lassen.

stoxx 1. Mai 2009 19:14

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Dafür ist Currency eben 100% genau. Bei Addition und Subtraktion. Und da sind 0.1 Euro - 0.1 Euro garantiert = 0,0000!

....

Wie schon gesagt, ist das nur ein getarnter Integer. Somit fällt die Verwendung des Datentyps Currency unter den Tipp Ganzzahlen zu verwenden.

ich glaube, ihr täuscht Euch da gewaltig. Currency ist nur vom Typ Extended.
Vielleicht mit 8 Byte etwas ganauer als Double, ja .. aber immer noch ungenau .. folgendes Beispiel demonstriert es ...




Delphi-Quellcode:
var
 cStart, c1, cadd : Currency;

begin



cStart := 10000000000000;
cadd := 0.1;
c1 := cStart;

for i := 1 to 10 do begin
     c1 := c1 + eAdd;

end; // for i


if          c1 = 10000000000001 then
        ShowMessage('Gleich = ');


if SameValue(c1, 10000000000001) then
        ShowMessage('Gleich SameValue ');

man sieht auch, wenn man z.b. rechnet:

c1 := c1 + cAdd;


und ins CPU Fenster geht, dass dort steht:

uFrm_Main_XM.pas.1201: cas c1 := c1 + eAdd;
008BFEC1 DBAD90FEFFFF fld tbyte ptr [ebp-$00000170]
008BFEC7 D80DBC0D8C00 fmul dword ptr [$008c0dbc]
008BFECD DFAD28FFFFFF fild qword ptr [ebp-$000000d8]
008BFED3 DEC1 faddp st(1)
008BFED5 DFBD28FFFFFF fistp qword ptr [ebp-$000000d8]




ich kenn mich zwar in Assembler nicht wirklich gut aus, aber "faddp" sind definitiv Fließkommezahlen



..

himitsu 1. Mai 2009 22:29

Re: Fließkommazahlen auf Gleichheit prüfen
 
Currency ist ein skalierter Int64, nur das er über die FPU berechnet wird.

Delphi-Quellcode:
Procedure TForm2.Button1Click(Sender: TObject);
  Var C: Currency;
    i: Int64 absolute C;
    i2: Integer;

  Begin
    For i2 := 0 to 100 do Begin
      C := i2 * 0.0531;
      Label1.Caption := CurrToStr(C);
      Label2.Caption := FloatToStr(i / 10000) + '   ' + IntToStr(i2);
      Application.ProcessMessages;
      Sleep(250);
    End;
  End;

stoxx 2. Mai 2009 00:37

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von himitsu
Currency ist ein skalierter Int64, nur das er über die FPU berechnet wird.


hmmm ... warum rechnet er dannn mein Beispiel von oben nicht richtig?

himitsu 2. Mai 2009 10:10

Re: Fließkommazahlen auf Gleichheit prüfen
 
also bei mir sieht es so aus ... k.A. wo bei dir das fmul her kommt :gruebel:
Delphi-Quellcode:
asm
  // c1 := c1 + cAdd;
  fild qword ptr [&c1]
  fild qword ptr [&cAdd]
  faddp st(1)
  fistp qword ptr [&c1]
  wait

  // if c1 = Extended(10000000000001) then
  fld tbyte ptr [10000000000001]
  fild qurd ptr [&c1]
  fcompp
  fstsw ax
  sahf
  jnz ...
end;
vielleicht fällt dir das fi... bzw. f.. auf > i für Integer

ein Problem seh ich nur beim Vergleich ... wo Delphi aus dem 10000000000001 einen Extended macht ... typlose Umwandlungen wesehn nur in Integertypen und Extended (nach Currency wandelt Delphi keine Konstanten von alleine um)

Dipl Phys Ernst Winter 2. Mai 2009 19:11

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Ich bräuchte aber ein entsprechende Beispiel, beim dem die Abfrage auf Gleichheit nicht funktioniert
Bitt sehr:
Delphi-Quellcode:
var
  a, b: extended
begin
  a:= 0.9;
  b:= 0.09;
  if 0.1*a=b then lblIstGleich.Caption:= 'ist gleich'
             else lblIstGleich.Caption:= 'ist ungleich';
end;

Korrekt treibt man beim Vergleich von Gleitkommazahlen Epsilontik:

const
  eps= 1e-14;              // Definiert eine Umgebung für Rundungsfehler
var
  a, b: extended;
begin
  if a<-eps then....       // a<0
  if Abs(a)<eps then...    // a=0 
  if a>eps then...         // a>0
  if Abs(a-b)<eps then     // a=b
[edit=mkinzler]Delphi-Tag eingefügt Mfg, mkinzler[/edit]

stoxx 3. Mai 2009 13:35

Re: Fließkommazahlen auf Gleichheit prüfen
 
edit
War ein Denkfehler, ich glaub, jetzt hab ichs verstanden :-)
der Wert steht zwar drin, kann aber wiederum nur mit einer anderen Currency verglichen werden, weil wie Himitsu schon sagte, der Vergleich schief geht.

Delphi-Quellcode:
var

 cStart, c1, c2, c3, c4, cmax, cadd : Currency;

 iAbsolute : Int64  absolute c1;

begin



cStart := 10000000000000;
cadd := 0.1;
c1 := cStart;

for i := 1 to 10 do begin
     c1 := c1 + cAdd;
end; // for i

ShowMessage(IntToStr(iAbsolute)); // <- hier erscheint: 100000000000010000 (korrekt)

if          c1 = 10000000000001 then
        ShowMessage('Gleich = ');

stoxx 3. Mai 2009 14:00

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von himitsu
also bei mir sieht es so aus ... k.A. wo bei dir das fmul her kommt :gruebel:


Ich glaube, das war mein Fehler *duck* .. wenn man in das Quelltextbeispiel guckt, dann steht dort "eAdd" und nicht "cAdd" wie geplant .. eAdd war aber dummerweise ein Extended .. und das fmul ist die Multiplikation mit dem Skalierungsfaktor bei Currency (10000)
sowas blödes .. :freak:
ich nehm alles zurück, was ich zu Currency gesagt habe :-) :wall:

himitsu 3. Mai 2009 14:04

Re: Fließkommazahlen auf Gleichheit prüfen
 
Zitat:

Zitat von stoxx
ich nehm alles zurück, was ich zu Currency gesagt habe :-) :wall:

:angel:


und drum hatte ich im anderem Thread auch 'ne reine Integerversion gepostet, da die FPU bzw. die Floattypen halt einiges an Ungenauigkeit mit sich bringt.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:00 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz