2010-05-05 7 views
10

Sto usando C++ in Visual Studio Express per generare alberi di espressioni casuali da utilizzare in un tipo di programma di algoritmo genetico.Come si usa try ... catch per rilevare errori in virgola mobile?

Poiché sono casuali, gli alberi spesso generano: divisione per zero, overflow, underflow e ritorno di "inf" e altre stringhe. Posso scrivere handler per gli archi, ma la letteratura mi ha lasciato perplesso riguardo agli altri. Se ho capito bene, devo prima impostare alcune bandiere?

Sarebbe gradito il consiglio e/o un puntatore ad alcune pubblicazioni. Modifica: i valori restituiti nella variabile doppia sono 1. # INF o -1. # IND. Ho sbagliato a chiamarli stringhe.

+0

C++ non detta nessuna di queste operazioni dovrebbe generare un'eccezione. Conducono a comportamenti indefiniti. (Che potrebbe causare un arresto anomalo o generare un'eccezione o non fare nulla, o ...) – GManNickG

+1

Ma C99 e POSIX specificano tali elementi e forniscono un'interfaccia di eccezione numerica. Tuttavia, non è chiaro se Peter abbia il controllo sui numeri reali, se sta ottenendo delle stringhe piuttosto che degli infiniti FP. – Potatoswatter

risposta

6

Sei sicuro di voler catturarli invece di ignorarli? Supponendo che si vuole solo ignorarli:

vedere questo: http://msdn.microsoft.com/en-us/library/c9676k6h.aspx

Per la maschera _MCW_EM, aprendo la maschera imposta l'eccezione, che permette l'eccezione di hardware; l'impostazione della maschera nasconde l'eccezione.

Così si sta andando a voler fare qualcosa di simile:

#include <float.h> 
#pragma fenv_access (on) 

void main() 
{ 
    unsigned int fp_control_word; 
    unsigned int new_fp_control_word; 

    _controlfp_s(&fp_control_word, 0, 0); 

    // Make the new fp env same as the old one, 
    // except for the changes we're going to make 
    new_fp_control_word = fp_control_word | _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW | _EM_INEXACT; 
    //Update the control word with our changes 
    _controlfp_s(&fp_control_word, new_fp_control_word, _MCW_EM) 


} 

Alcuni di confusione qui potrebbe essere passato l'uso della parola "eccezione". In C++, di solito si riferisce al sistema di gestione delle eccezioni incorporato nella lingua. Le eccezioni a virgola mobile sono una bestia completamente diversa. Le eccezioni richieste per supportare un FPU standard sono tutte definite in IEEE-754. Questi si verificano all'interno dell'unità a virgola mobile, che può fare cose diverse a seconda di come sono impostati i flag di controllo dell'unità a virgola mobile. Di solito accade una delle due cose: 1) L'eccezione viene ignorata e la FPU imposta un flag che indica un errore nei suoi registri di stato. 2) L'eccezione non viene ignorata dalla FPU, quindi viene generato un interrupt e viene chiamato qualsiasi tipo di gestore di interrupt per gli errori in virgola mobile.Di solito questo fa qualcosa di carino per te, che ti porta a interrompere quella riga di codice nel debugger o a generare un file core.

È possibile trovare maggiori su IEE-754 qui: http://www.openwatcom.org/ftp/devel/docs/ieee-754.pdf

Alcuni riferimenti in virgola mobile supplementari: http://docs.sun.com/source/806-3568/ncg_goldberg.html http://floating-point-gui.de/

+1

Supponendo che il codice sia in esecuzione su un processore x86 ... – dthorpe

+1

dthorpe: in realtà, non presumo che sia in esecuzione su un processore x86. I documenti MSDN affermano esplicitamente che _controlfp_s è il nuovo modo "indipendente dalla piattaforma" per farlo. Sospetto per piattaforma indipendente che si riferiscano al tipo di CPU, poiché ovviamente non tutti i sistemi operativi x86 hanno questa funzione - finché lo fa su una piattaforma Windows, dovrebbe essere OK. – George

+2

Sappi anche che ho visto librerie in conflitto sull'impostazione della parola di controllo a virgola mobile, ognuna delle quali impone la sua preferenza sull'altra. Quasi come i driver di stampa cambiano le impostazioni locali nel feed di un programma ... –

-1

mai provato, ma assumendo divisione per zero genera un'eccezione, si può solo avvolgere il codice in un blocco try/catch in questo modo:

provare {// potenziale divisione per zero ...} catch (...) { // ... cattura tutte le eccezioni }

+0

Questo è sbagliato, perché l'ambiente di runtime C++ non ti aiuterà qui. Non genererà un'eccezione per nessun motivo oltre a quello che il programmatore ha specificamente chiesto di lanciare. – wilhelmtell

1

l'ambiente di runtime C++ non vi aiuterà il minimo qui. Devi eseguire questi controlli da solo, esplicitamente nel tuo codice. A meno che le funzioni che chiamate non stiano facendo questi controlli, nel qual caso dipende da come si comportano in caso di errore.

Mi spiego:

double divide(double a, double b) { 
    return a/b; // undefined if b is zero 
} 

Dovrebbe essere infatti

double divide(double a, double b) { 
    if(b == 0) { 
     // throw, return, flag, ... you choose how to signal the error 
    } 
    return a/b; // b can't possibly be zero here 
} 

Se il codice che non riesce a divisione per zero e tale non è tuo, allora si dovrà scavare più profondo per trovare ciò che fa nel caso di una minaccia per un errore. Getta? Imposta una bandiera? Chiedi all'autore e/o leggi la fonte.

Ecco un esempio per un'eccezione:

struct bad_value : public std::exception { }; 

double divide(double a, double b) { 
    if(b == 0) throw bad_value("Division by zero in divide()"); 
    return a/b; // b can't possibly be zero here 
} 

// elsewhere (possibly in a parallel universe) ... 

    try { 
     double r1 = divide(5,4); 
     double r2 = divide(5,0); 
    } catch(bad_value e) { 
     // ... 
    } 
+0

Grazie. Sembra che dovrò implementare il mio "tiro". –

8

Secondo http://msdn.microsoft.com/en-us/library/aa289157%28v=vs.71%29.aspx#floapoint_topic8,
sembra possibile gettare eccezioni C++ in MSVC

Creare le classi di eccezioni:

class float_exception : public std::exception {}; 
class fe_denormal_operand : public float_exception {}; 
class fe_divide_by_zero : public float_exception {}; 
class fe_inexact_result : public float_exception {}; 
class fe_invalid_operation : public float_exception {}; 
class fe_overflow : public float_exception {}; 
class fe_stack_check : public float_exception {}; 
class fe_underflow : public float_exception {}; 

Mappa C++ gettare le eccezioni FPU utilizzando gestore delle eccezioni strutturata

void se_fe_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp) 
{ 
    switch (u) 
    { 
    case STATUS_FLOAT_DENORMAL_OPERAND: throw fe_denormal_operand(); 
    case STATUS_FLOAT_DIVIDE_BY_ZERO:  throw fe_divide_by_zero(); 
    etc... 
    }; 
} 
. . . 
_set_se_translator(se_fe_trans_func); 

È quindi possibile utilizzare provare cattura

try 
{ 
    floating-point code that might throw divide-by-zero 
    or other floating-point exception 
} 
catch(fe_divide_by_zero) 
{ 
    cout << "fe_divide_by_zero exception detected" << endl; 
} 
catch(float_exception) 
{ 
    cout << "float_exception exception detected" << endl; 
} 
+2

Questa dovrebbe essere la risposta approvata: corrisponde alla domanda originale (la risposta accettata in pratica dice "non dovresti preoccuparti" e la seconda dice "non puoi" (che non è corretto). – Deimos