2011-11-08 19 views
8

La mia app è scritta in Delphi5. Sto usando madExcept per rintracciare i bug. Ho rintracciato un'eccezione "Dvision a virgola mobile per zero", dove non dovrebbe essere. Il segmento di codice, dove viene generato, è il seguente:Divisione in virgola mobile con eccezione zero in Delphi5

val:=100*Power(1.25,c); 

dove 'c' ha sempre il valore '1'.

L'analisi dello stack del registro:

main thread ($338f8): 
00403504 +010 MyApp.exe System 1970 +5 @FRAC 
00479148 +058 MyApp.exe Math    Power 
007ae8a6 +262 MyApp.exe MyClass 1962 +36 TMyClass.FormMouseWheel 

ho avuto un'altra eccezione a un certo punto, in cui una divisione ha avuto luogo, ma il divisore era una variabile, che aveva anche il valore '1' quando il si è verificata un'eccezione. Che ero in grado di eseguire il debug e riprodurre.

La mia domanda: cosa mi manca? Ci sono dei falsi positivi sulla divisione in virgola mobile di cui non sono a conoscenza?

Inoltre: non utilizzo alcuna DLL C++ nei punti di eccezione in quanto tendono a gestire le divisioni FP in modo diverso (restituendo NaN o +/- INF anziché sollevare un'eccezione).

Qualsiasi suggerimento apprezzato.

+3

Non sembra molto plausibile. Non penso che i tuoi strumenti di debug ti indirizzino nel posto giusto, o forse le variabili non trattengono ciò che pensi di essere. –

+0

Non ricordo se D5 lo avesse già, ma hai provato a controllare cosa sta succedendo nella vista CPU/FPU quando quel codice viene eseguito? –

+0

@ldsandon Ovviamente D5 già ha permesso di usare Alt-F2 ed entrare nella vista CPU/FPU. Buona idea. Ma suppongo che un'eccezione FPU non gestita si romperà nel codice 'System._Frac'. –

risposta

8

Ho appena provato il seguente codice:

procedure TTTest.FormCreate(Sender: TObject); 
var v: extended; 
    one: extended; 
begin 
    one := 1.0; 
    v := 100*Power(1.25,one); 
end; 

E 'appena compilato ed eseguito come previsto in Delphi 5.

La mia ipotesi è che la divisione per bandiera zero può essere impostato al di fuori il codice (anche se non si collega al codice C++, chiamare Direct X o simile potrebbe avere lo stesso effetto), ma generato in seguito, in _Frac.

L'unica chiamata a Frac nell'implementazione standard di Power() è di provare Frac(Exponent) = 0.0.

C'era una modifica dell'attuazione di Frac tra Delphi 5 e Delphi 6.

Ecco la versione Delphi 5:

procedure  _FRAC; 
asm 
    FLD  ST(0) 
    SUB  ESP,4 
    FSTCW [ESP] 
    FWAIT 
    FLDCW cwChop 
    FRNDINT 
    FWAIT 
    FLDCW [ESP] 
    ADD  ESP,4 
    FSUB 
end; 

Ecco la versione Delphi 6:

procedure  _FRAC; 
asm 
    FLD  ST(0) 
    SUB  ESP,4 
    FNSTCW [ESP].Word  // save 
    FNSTCW [ESP+2].Word // scratch 
    FWAIT 
    OR  [ESP+2].Word, $0F00 // trunc toward zero, full precision 
    FLDCW [ESP+2].Word 
    FRNDINT 
    FWAIT 
    FLDCW [ESP].Word 
    ADD  ESP,4 
    FSUB 
end; 

Dal codice precedente, scoprirai che i seguenti comandi hanno causato il ritardo delle eccezioni prima del rilascio di Delphi 6: Trunc, Frac, Ceil .

Quindi immagino che abbia affrontato un problema con Delphi 5, che è stato corretto con Delphi 6.Potrebbe essere necessario utilizzare la propria versione di potenza, come questo:

function Power(Base, Exponent: Extended): Extended; 
begin 
    if Exponent = 0.0 then 
    Result := 1.0    { n**0 = 1 } 
    else if (Base = 0.0) and (Exponent > 0.0) then 
    Result := 0.0    { 0**n = 0, n > 0 } 
    else 
    Result := Exp(Exponent * Ln(Base)) 
end; 
+0

Ottima risposta che ha puntato direttamente al problema per me. Minor picking, ma la risposta potrebbe essere migliorata con commenti che spiegano cosa sta facendo ciascuna linea di assemblaggio, almeno per le linee chiave. In alternativa, riepilogare cosa fa ogni procedura _FRAC (spingere vecchio CW in pila, impostare nuovo CW, fare operazione, pop vecchio stack CW, quel genere di cose). Sarebbe di aiuto per quelli di noi non esperti nell'assemblaggio x86. :) –

2

Non una risposta definitiva con qualsiasi mezzo, ma ...

eccezioni FPU relativi dove non ci dovrebbe essere, potrebbe essere correlato alla pila FPU non essere eliminato correttamente. Abbiamo avuto problemi simili in una fase, anche se abbiamo riscontrato eccezioni di operazioni in virgola mobile non valide.

Questo articolo: Delphi bug of the day: FPU stack leak dove qualcuno ha rintracciato il motivo di un'eccezione Operazione in virgola mobile invalida generata da S := S + '*';, ci ha aiutato a risolvere il problema.