Bug im FPC? (bei rekursiven Funktionen)

Für Fehler in Lazarus, um diese von anderen verifizieren zu lassen.
Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Bug im FPC? (bei rekursiven Funktionen)

Beitrag von Euklid »

Hallo Leute!
monta hat geschrieben:[...]So ist es möglich, diese [Bugs] von anderen Nutzern verifizieren zu lassen, bevor man einen Eintrag im Bugtracker vornimmt.
Und genau das möchte ich machen:

Ich habe einen Bug gefunden, von dem ich glaube, dass er im FPC steckt. Habe ihn inzwischen separieren können und eben eine Funktion geschrieben, mit dem er mit (glaube) allen FPC-Versionen reproduziert werden kann. Wäre Euch dankbar, wenn Ihr das mal prüfen könntet:

Code: Alles auswählen

function TForm1.crash(n:integer):real;
begin
  case n of
  0:    Result:=0;
  1..20: Result:=crash(n-1)+crash(n-1);
  end;
end;
Diese Funktion führt genau dann zum crash, wenn der Parameter n größer als 7 ist, sonst nicht. Dabei erscheint die Fehlermeldung:
Project raised exception class 'External: SIGFPE'
Schreibt man die Funktion crash wie folgt, kommt es zu keinem Absturz des Programms:

Code: Alles auswählen

function TForm1.dontcrash(n:integer):real;
var mem1, mem2:real;
begin
  case n of
  0:    Result:=0;
  1..20: begin
            mem1:=dontcrash(n-1);
            mem2:=dontcrash(n-1);
            Result:=mem1+mem2;
          end;
  end;
end;
Man sieht: Die Funktion crash und dontcrash unterscheiden sich nur dadurch, dass die Summanden vorher in mem1 und mem2 zwischengespeichert wurden.

Ich vermute, es handelt sich um einen Bug im FPC und nicht in Lazarus.

Was meint Ihr? Konntet ihr den Abbruch des Programms mit der Funktion crash reproduzieren?


Viele Grüße, Euklid

Getestet habe ich die beiden Funktionen mit den folgenden Systemen:
Debian etch, Lazarus 0.9.22, FPC 2.0.4
PCLinuxOS, Lazarus 0.9.23 (Snapshot), FPC 2.2.0 (Snapshot)
Zuletzt geändert von Euklid am Di 9. Okt 2007, 23:03, insgesamt 1-mal geändert.

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

Verifiziert!

Es gibt bei mir nen FloatingPointError, wenn n größer 7.

Getestet ohne Debugger unter:
Windows XP >> Lazarus SVN, FPC 2.2.0
Johannes

Benutzeravatar
theo
Beiträge: 10922
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Hmm, bei mir nicht. Habe auch alle Check noch eingeschalten.
Gibt 0 zurück.

{$O+}
{$R+}
{$S+}

function crash(n:integer):real;
begin
case n of
0: Result:=0;
1..20: Result:=crash(n-1)+crash(n-1);
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Caption:=FloatToStr(crash(19));
end;

FPC 2.0.4 OpenSuSE 10.2

_Bernd
Beiträge: 145
Registriert: Di 13. Feb 2007, 11:16

Re: Bug im FPC? (bei rekursiven Funktionen)

Beitrag von _Bernd »

Euklid hat geschrieben: Ich vermute, es handelt sich um einen Bug im FPC und nicht in Lazarus.

Was meint Ihr? Konntet ihr den Abbruch des Programms mit der Funktion crash reproduzieren?
nein, nicht mit FPC 2.2.0 als Konsolenanwendung unter Windows 98. Ich würde vorschlagen, daß Du mal ein minimales Lazarus-Projekt erzeugst und hochlädst. Dann sieht man auch gleich mal Deine Compiler-Schalter.

Gruß, Bernd.

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Re: Bug im FPC? (bei rekursiven Funktionen)

Beitrag von Euklid »

Danke schonmal.
_Bernd hat geschrieben: nein, nicht mit FPC 2.2.0 als Konsolenanwendung unter Windows 98.
Interessant!
Ich würde vorschlagen, daß Du mal ein minimales Lazarus-Projekt erzeugst und hochlädst. Dann sieht man auch gleich mal Deine Compiler-Schalter.

Ok, ich habe ein Testprojekt hier angehängt: In dem Textfenster lässt sich das Argument n festlegen.
Dateianhänge
bug.tar.gz
(3.72 KiB) 69-mal heruntergeladen

Benutzeravatar
theo
Beiträge: 10922
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Joh, als Methode von Form1 crashts, nicht aber als normale Funktion.
function crash(n:integer):real;

_Bernd
Beiträge: 145
Registriert: Di 13. Feb 2007, 11:16

Beitrag von _Bernd »

Sieht tatsächlich so aus, als ob es ein FPC 2.2.0-Bug ist. Wenn die Funktion "crash" eine Methode einer Klasse ist, dann gibt es den Absturz. Ist "crash" eine stinknormale Funktion (habe Dein Testprojekt erweitert) dann läuft es.

Das Projekt läuft mit Delphi 5 problemlos. Ich werde jetzt nochmal mit FPC 2.0.4 testen...

Gruß, Bernd.
Dateianhänge
bug.zip
(2.86 KiB) 75-mal heruntergeladen

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

theo hat geschrieben:Joh, als Methode von Form1 crashts, nicht aber als normale Funktion.
function crash(n:integer):real;
so hatte ich es aber bei meinem Versuch auch.

nahezu exakt wie du oben schon geschrieben hast, und es hat gecrasht:

Code: Alles auswählen

function crash(n:integer):real;
begin
case n of
0: Result:=0;
1..20: Result:=crash(n-1)+crash(n-1);
end;
 
procedure TForm1.Button1Click(Sender: TObject); 
begin
crash(8);
end;
Johannes

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Beitrag von Euklid »

theo hat geschrieben:Joh, als Methode von Form1 crashts, nicht aber als normale Funktion.
function crash(n:integer):real;
Oh. Sorry. Dachte, dass es sich dabei im Prinzip um dasselbe handelt, mein Fehler.

Übrigends: Wenn man den Typ der Funktion zu "integer" ändert, tritt der Bug _nicht_ auf. Nur beim Typ real und extended, interessanterweise.

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Beitrag von Euklid »

monta hat geschrieben: so hatte ich es aber bei meinem Versuch auch.

nahezu exakt wie du oben schon geschrieben hast, und es hat gecrasht:
Ja, sorry. Am Besten werde ich meinen ersten Eintrag zu TForm1.crash korrigieren. Wie gesagt, ich dachte, das macht keinen technischen Unterschied.

Was meint ihr: Eher ein FPC oder ein Lazarus-Bug?

monta
Lazarusforum e. V.
Beiträge: 2809
Registriert: Sa 9. Sep 2006, 18:05
OS, Lazarus, FPC: Linux (L trunk FPC trunk)
CPU-Target: 64Bit
Wohnort: Dresden
Kontaktdaten:

Beitrag von monta »

ich meinte eigentlich theo ;)

es crasht, sobald die Funktion im interface aufgeführt ist.

Ändert man Bernds anwendung und schreibt die Funktion ins interface, crasht es auch, auch wenns nicht TForm unterstellt ist:

Code: Alles auswählen

function nocrash(n:integer):real;
 
var
  Form1: TForm1; 
 
implementation
 
{ TForm1 }
 
function nocrash(n:integer):real;
begin
  case n of
  0:    Result:=0;
  1..100: Result:=nocrash(n-1)+nocrash(n-1);
  end;
end;
Johannes

Benutzeravatar
theo
Beiträge: 10922
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Sicher eine Angelegenheit des Compilers.
Ich frage mich aber, ob das ein Bug ist.
Sowas habe ich noch nie gesehen, dass die Rekursion zweimal aufgerufen wird.
Hast du überhaupt noch einen Plan, was hier abgeht?
Ich meine was nach was kommt?
kann doch gut sein, dass er erst über den ersten Ausdruck rekursiert bis zum Ende:
crash(n-1)
und erst wenn er damit durch ist zu
+crash(n-1) kommt.

Also ungeheurlich sieht das schon aus.

_Bernd
Beiträge: 145
Registriert: Di 13. Feb 2007, 11:16

Beitrag von _Bernd »

theo hat geschrieben:Also ungeheurlich sieht das schon aus.
:-)

Benutzeravatar
theo
Beiträge: 10922
Registriert: Mo 11. Sep 2006, 19:01

Beitrag von theo »

Vielleicht eher "abenteuerlich" ;-)

Euklid
Lazarusforum e. V.
Beiträge: 2808
Registriert: Fr 22. Sep 2006, 10:38
OS, Lazarus, FPC: Lazarus v2.0.10, FPC 3.2.0
Wohnort: Hessen
Kontaktdaten:

Beitrag von Euklid »

theo hat geschrieben:Sicher eine Angelegenheit des Compilers.
Ich frage mich aber, ob das ein Bug ist.
Wenn nicht, dann ist es auf jeden Fall eine inkompatibilität zu Delphi... (nach Bernds Tests)
Sowas habe ich noch nie gesehen, dass die Rekursion zweimal aufgerufen wird.
Hast du überhaupt noch einen Plan, was hier abgeht? Ich meine was nach was kommt?
Es hat auch eine ganze Weile gedauert, bis ich den Bug in so reiner Form wie in den crash-Funktionen separieren konnte.
Für Lexart habe ich eine Funktion geschrieben, die die Funktionswerte beliebiger Funktionen mit einer wesentlich schneller Geschwindigkeit berechnet als zuvor. Nur dummerweise hatte diese Funktion Probleme mit sehr einfachen Termen, z.B. '1+1+1+1+1+1+1+1+1' ergab obige Fehlerausgabe.

Die Fehlerhafte Routine sah dabei so aus:

Code: Alles auswählen

function TAnalyse.FunkWert_NM(funktion:TermTyp; XWert:extended; Pointer:cardinal):extended;
begin
  CASE funktion.TFeld[Pointer].Opera OF
    001: Result:=funktion.TFeld[Pointer].GlKoZa;                                              //001=Zahl
    002: Result:=XWert;                                                                       //002=Variable
    003: Result:=(-1)*FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0]);        //003=Negation
    004: begin                                                                               //004=Addition
           Result:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0])+FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[1]);
         end;
    005: begin                                                                                //005=Multiplikation
 
                   Result:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0])*FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[1]);
         end;
 
// ... weitere Zeilen...
    else Result:=1;
  end;
end; //Funwert_NM
Die problematische Rekursion befand sich an 2 Stellen: nach 004 und 005.

Nach langem debuggen habe ich heute aus Verzweiflung dann das ausprobiert:

Code: Alles auswählen

function TAnalyse.FunkWert_NM(funktion:TermTyp; XWert:extended; Pointer:cardinal):extended;
var Arg0,Arg1:extended;  //Zwischenspeicher
begin
  CASE funktion.TFeld[Pointer].Opera OF
    001: Result:=funktion.TFeld[Pointer].GlKoZa;                                              //001=Zahl
    002: Result:=XWert;                                                                       //002=Variable
    003: Result:=(-1)*FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0]);        //003=Negation
    004: begin                                                                                //004=Addition
           Arg0:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0]);
           Arg1:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[1]);
           Result:=Arg0+Arg1;
         end;
    005: begin                                                                                //005=Multiplikation
           Arg0:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[0]);
           Arg1:=FunkWert_NM(funktion,XWert,funktion.TFeld[Pointer].Argument[1]);
           Result:=Arg0*Arg1;
         end;
 
// ... weitere Zeilen...
    else Result:=1;
  end;
end; //Funwert_NM
... und siehe, es funktionierte :)

Naja, und dann hab ich obige crash-Funktion zum Test verwendet. :)

PS
@monta: Wenn ich Lazarus-Code im nicht-vollen Editor editiere, gibts irgendwie einen Zeichensalat?

Antworten