![]() |
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:
Ich bräuchte aber ein entsprechende Beispiel, beim dem die Abfrage auf Gleichheit nicht funktioniert.
var
a: Double; b: Double; begin a := 19.3; b := 2 * 9.65; if a = b then Writeln('gleich') else Writeln('ungleich'); Readln; |
Re: Fließkommazahlen auf Gleichheit prüfen
bei mir ist folgendes ungleich:
Delphi-Quellcode:
Edit1:
var
a, b: Real; begin a := 12345789.987654321; b := 24691357.9975308642 / 2; if a = b then Writeln('gleich') else Writeln('ungleich'); Readln; end. Nach der Delphi 7 PE Hilfe ist Double = Real. |
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. |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
daniel muss wohl gemeiner weise meine post nach
Code:
parsen lassen und es durch
/ 2
Code:
ersetzen lassen.
/ 5
|
Re: Fließkommazahlen auf Gleichheit prüfen
10.0/3.0 und 10*(1.0/3.0) tun genau das selbe bei weniger Schreibaufwand :)
|
Re: Fließkommazahlen auf Gleichheit prüfen
Liste der Anhänge anzeigen (Anzahl: 1)
Zitat:
Delphi-Quellcode:
Ausgabe:
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;
Code:
Du hast dich vertan. Zwei mal 12345789.987654321 ist nicht 24691357.9975308642 sondern 24691579.975308642.
a: 1.23457899876543E+0007
b: 1.23457899876543E+0007 gleich @Dax auch dein Beispiel ergibt 'gleich'. EDIT: Übrigens das Beispiel von dieser Seite: ![]() Im Anhang mal mein Artikel als PDF zu der Prolematik. |
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. ;)
|
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.
|
Re: Fließkommazahlen auf Gleichheit prüfen
Ist der Artikel denn so verständlich und sachlich richtig oder gibt es noch Ergänzungen / Korrekturen?
|
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
|
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. |
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. ;)
|
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: ![]() Und im Anhang das endgültige PFD. |
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:
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):
Var
a,b : Double; begin a := 0.1; b := a - 0.1; if b=0 then ShowMessage('gleich') else ShowMessage ('ungleich'); end;
Delphi-Quellcode:
Aber das hier wiederum nicht :mrgreen:
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;
Delphi-Quellcode:
Weil Konstanten per definitionem nur Platzhalter sind.
Const
C = 0.1; Var a,b : Double; begin a := C; b := a - C; if b=0 then ShowMessage('gleich') else ShowMessage ('ungleich'); end; 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] |
Re: Fließkommazahlen auf Gleichheit prüfen
wo ist jetzt der unterschied? C wird in beiden Fällen definiert aber nirgends verwendet :gruebel:
|
Re: Fließkommazahlen auf Gleichheit prüfen
:oops: und deswegen auch:
Delphi-Quellcode:
While Time < StrToTime ('09:30') Do
Alzaimar.CanPostImForum := False |
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 |
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. |
Re: Fließkommazahlen auf Gleichheit prüfen
alzaimar:
Zitat:
Grüße Frank |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
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! |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
|
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
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 .. |
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; |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
hmmm ... warum rechnet er dannn mein Beispiel von oben nicht richtig? |
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:
vielleicht fällt dir das fi... bzw. f.. auf > i für Integer
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; 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) |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
Delphi-Quellcode:
[edit=mkinzler]Delphi-Tag eingefügt Mfg, mkinzler[/edit]
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 |
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 = '); |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
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: |
Re: Fließkommazahlen auf Gleichheit prüfen
Zitat:
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 20:23 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz