Autor Beitrag
Praktiker
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 14.05.16 16:16 
Hallo Forum,
ich folgendes Problem welches ich mir nicht erklären kann:
Wenn ich in einer Funktion die in einer DLL ist eine Exception habe wird diese nicht immer
behandelt. Mal klappt das mehrere hundert mal, manchmal nur einmal...


Es handelt sich un eine Funktion wie diese:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
function Dividexy(x,y : Extended) :extended; stdcall;
begin
  try
    Result := x / y;
  except
    Result := 0.0;
  end;
end;


Wenn hier durch 0 geteilt wird (was ja eigentlich abgefangen sein sollte)
bekomme ich manchmal NaN zurück !!!
Kann mir das jemand erklären?

Getestet unter WIn7 64Bit und Delphi XE10
Viele grüsse
Praktiker

Moderiert von user profile iconChristian S.: Delphi-Tags hinzugefügt
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 14.05.16 19:04 
Hallo und :welcome:

An der Stelle try..except zu benutzen ist nicht wirklich sinnvoll. Du weißt ja schließlich wann der Fehler auftritt, kannst also schlicht prüfen, ob y = 0 ist. Dafür gibt es die Funktion IsZero.

Es könnte sein, dass y durch Rundungsfehler nur sehr nahe dran an Null ist, aber nicht exakt Null. Dann geht die Division durch, es kommt aber kein sinnvolles Ergebnis heraus.

Deshalb:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
function Dividexy(x, y: Extended): Extended; stdcall;
begin
  if IsZero(Y) then
    Result := 0
  else
    Result := x / y;
end;


Nebenbei: In einer DLL Funktion sollte man Extended besser nicht verwenden, da es den Datentyp in anderen Programmiersprachen nicht gibt. Somit können diese solche Funktionen nicht nutzen.
Praktiker Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: So 15.05.16 13:47 
Hallo jaenicke,

danke für die ausführliche Anwort.
Sowohl die DLL als auch die Anwendung sind mit Delphi XE10 erstellt so das der Datentyp Extended keine Probleme bereiten sollte,
aber danke für den Hinweis, werde ich bei DLL zukünftig beachten.
Mein eigentliches Problem ist aber noch nicht gelöst, warum kann aus diese Funktion manchmal ein NaN zurück gegeben werden?
Werden Exceptions in einer dll anders behandelt?

Grüsse
Praktiker
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 15.05.16 14:22 
Ich habe ja geschrieben:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:

Es könnte sein, dass y durch Rundungsfehler nur sehr nahe dran an Null ist, aber nicht exakt Null. Dann geht die Division durch, es kommt aber kein sinnvolles Ergebnis heraus.

Wenn y zum Beispiel 0,0000000...1 ist, funktioniert die Division zwar, aber das Ergebnis ist trotzdem NaN, weil es Richtung unendlich geht.

Deshalb gibt es keine Exception.

Deshalb solltest du eben solche Fehler gezielt abfangen statt sich auf Exceptions zu verlassen.

Für diesen Beitrag haben gedankt: erfahrener Neuling
Praktiker Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Di 17.05.16 15:16 
Hallo jaenicke,

ich habe das ganze nochmal getestet und bin leider nicht weiter gekommen.
Wenn ich die Funktion (nun mit double statt extended) aufrufe und durch null teile bekomme cih beim 1. mal 0 zurück, wie erwartet.
Beim 2. aufruf bekomme ich -NaN zurück und auch keine Exception meldung (Obwohl die DLL im Deubugger läuft).
Die Funktion wird mit Konstanten aufgerufen !!
Wie kann das sein?

Viele Grüße
Praktiker
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 17.05.16 22:48 
Hast du die Konstante denn als Typ Double definiert? Ansonsten wird ggf. doch umgewandelt.

Hast du denn mal mit IsZero versucht, ob es dann klappt?
Praktiker Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: Sa 21.05.16 15:27 
Hallo jaenicke,

mit isZero klappt es, aber darum geht es ja nicht direkt.
Schlimmer ist das wenn ich einmal NAN zurück bekommen habe auch die Funktion innerhalb der Anwendung (Form) nicht mehr geht.
Ich habe das ganze auf verschiedenen Betriebssystemen getestet.
Auf Windows XP läuft es wie erwartet auf Win 7 oder Win 10 nicht mehr.
Zur verdeutlichung habe ich mal eine Testprojektmappe angehangen.
Die Funktion innerhalb der Anwendung kann ich beliebige male aufrufen >5000.
Die Funktion in der DLL nur max 2000 mal dann gibt es ein NAN zurück und die Funktion in der Anwendung geht auch nicht mehr.
Falls die Codesite Komponenten nicht gefunden werden kann man diese auskommentiern, sind nur zur Debugausgabe.
Viele Grüsse
Praktiker
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 22.05.16 00:32 
Es scheint als ob die Exceptionmaske der FPU nicht korrekt gesetzt ist. Du hast da ja auch eine solche Variable im Quelltext schon drin, warst also schon auf der richtigen Spur. Es reicht aber nicht, diese in der Anwendung zu setzen.

Es sollte einmalig beim Laden der DLL reichen, ich habe es jetzt in der DLL der Einfachheit halber so gemacht:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
function Dividexy(x, y: double) : double;
begin
  SetExceptionMask(GetExceptionMask);
  try
    Result := x / y;
  except
    on e: Exception do
    begin
      Result := -1;
    end;
  end;
end;
Das scheint problemlos zu funktionieren, auch wenn ich nicht immer ein div by zero bekomme, sondern eine allgemeine ungültige Fließkommaoperation. Das wurde irgendwo auch schon einmal thematisiert, dass das so ist.

Warum es allerdings hilft die angeblich schon gesetzte Exceptionmaske zu setzen, kann ich dir nicht sagen. Eventuell wird diese nirgends initialisiert.
Praktiker Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: So 22.05.16 16:13 
Hallo jaenicke,
du hast recht ich hatte mir das mit der Exceptionmaske bereits angesehen kam aber auch damit nicht weiter.
Ich habe deinen vorschlag umgesetzt und getestet, leider ohne Erfolg.
Das ganze wird erst deutlich wenn man die Anwendung ohne Debugger aufruft und ein paar sekunden (weniger als eine Minute) laufen lässt.
1. DLL Laden
2. Beide checkboxen klicken
3. Warten
Dieses Verhalten kann so nicht richtig sein.
Viele Grüsse
Praktiker
Einloggen, um Attachments anzusehen!
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 22.05.16 16:21 
Was steht bei dir denn in der Exceptionmaske drin? Bei mir war div by zero korrekterweise nicht drin.

Korrekt oder nicht, der saubere Weg ist ohnehin sich bei solchen Rechenoperationen nicht auf Exceptions zu verlassen sondern die Bedingungen selbst zu prüfen. Da wir das immer machen, brauchen wir kein try..except und wir können genau ausgeben welcher Wert das Problem war.

Da du mit der kosmetischen Lösung (korrektes Exceptionhandling) wohl nicht weiterkommen wirst, weil du das Verhalten vermutlich nicht korrigieren kannst, bleibt wohl nur der saubere Weg.
Praktiker Threadstarter
Hält's aus hier
Beiträge: 6



BeitragVerfasst: So 22.05.16 16:46 
Die Exception ist eine EInvalidOP, das ist ok.

Aber irgendwann kommt sie halt zur Laufzeit nicht mehr.
Was viel schlimmer ist die Funktion die in der Anwendung, also nicht der DLL ist geht dann auch nicht mehr.
Laut Embacadero Wiki sind ide Exceptions innerhalb der DLL zu behandeln, aber was wenn es gar keine gibt?
Das Programm ist ja nur ein Beispiel um zu testen. in der echten Anwendungen sind ide BErechnungen aufwendiger und es wäre nicht schön wenn
man immer auf Iszero abfragen müsste.
Es geht ja einige 1000 male gut.... und unter WIN XP immer!
Mit dem setzen der Exceptionmask sieht es aber besser aus, 100 Prozentig ist das aber auch nicht.
Viiele Grüsse
Praktiker
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Mo 23.05.16 15:44 
Sprachexceptions in DLLs sind immer haarig. FPU-Exceptions auch. Wenn man das dann kombiniert, ist da so viel undefiniert...
Problematisch ist, dass du die Exception durchaus auch später in deinem Code bekommen kannst, sobald die Maske wieder stimmt und ein FWAIT ausgeführt wird.

Mit aktuellen Delphis kenne ich mich nicht aus, kann das sein, dass dort für das Exceptionhandling VectoredExceptionHandler benutzt werden wenn sie verfügbar sind, und das deswegen je nach Windowsversion mal geht oder nicht?

Du hattest Extended benutzt, wir sind also in einer Win32-Anwendung? Win64 hat da ein paar schönere Methoden, aber das dürfte hier wohl auch nur begrenzt weiterhelfen.

_________________
"The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."