Domanda

Ricerca on-line, ho trovato la seguente routine per il calcolo del segno di un galleggiante in formato IEEE. Questo potrebbe essere facilmente esteso ad un doppio, anche.

// returns 1.0f for positive floats, -1.0f for negative floats, 0.0f for zero
inline float fast_sign(float f) {
    if (((int&)f & 0x7FFFFFFF)==0) return 0.f; // test exponent & mantissa bits: is input zero?
    else {
        float r = 1.0f;
        (int&)r |= ((int&)f & 0x80000000); // mask sign bit in f, set it in r if necessary
        return r;
    }
}

( Fonte : `` segno veloce per 32 bit galleggia '' , Peter Schoffhauzer)

Sono stanco di usare questa routine, però, a causa delle operazioni binarie bit. Ho bisogno del mio codice per lavorare su macchine con diversi ordini di byte, ma non sono sicuro di come gran parte di questo previsto dalla norma IEEE, come non ho potuto trovare la versione più recente, pubblicato quest'anno. Qualcuno può dirmi se questo funzionerà, indipendentemente dall'ordine di byte della macchina?

Grazie, Patrick

È stato utile?

Soluzione

Come pensi che fabs() e fabsf() sono implementati su sistema, o per quella materia con il confronto costante 0? Se non è da ops bit per bit, è molto probabilmente perché gli scrittori compilatore non credo che sarebbe stato più veloce.

I problemi di portabilità con questo codice sono:

  1. float e int potrebbero non avere lo stesso endianness o anche la stessa dimensione. Da qui anche, le maschere potrebbe essere sbagliato.
  2. float potrebbe non essere la rappresentazione IEEE
  3. Si interrompe rigide regole di aliasing. Il compilatore è permesso assumere che un puntatore / riferimento ad un galleggiante e un puntatore / riferimento ad un int non possono punto nella stessa posizione di memoria. Così, per esempio, lo standard non garantisce che r viene inizializzato con 1.0 prima che venga modificato nella riga seguente. Potrebbe riordinare le operazioni. Questo non è speculazione, ea differenza (1) e (2) è definito, non è definito dall'implementazione, quindi non si può necessariamente solo guardare in su per il vostro compilatore. Con abbastanza ottimizzazione, ho visto GCC saltare l'inizializzazione delle variabili float che si fa riferimento solo attraverso un tipo puntatore-punned.

Vorrei prima fare la cosa più ovvia ed esaminare il codice emesso. Solo se che appare dubbia vale la pena di pensare a fare qualsiasi altra cosa. Non ho alcuna ragione particolare per pensare che so di più sulla rappresentazione bit per bit dei carri allegorici del mio compilatore non; -)

inline float fast_sign(float f) {
    if (f > 0) return 1;
    return (f == 0) ? 0 : -1;
    // or some permutation of the order of the 3 cases
}

[Edit: in realtà, GCC fa di qualcosa di un pasto di che, anche con -O3. Il codice emesso non è necessariamente lento, ma utilizza op virgola mobile quindi non è chiaro che è veloce. Quindi il passo successivo è quello di punto di riferimento, verificare se l'alternativa è più veloce su qualsiasi compilatore è possibile mettere le mani su, e in tal caso fare qualcosa che la gente porting il codice possono attivare con un #define o qualsiasi altra cosa, in base ai risultati del proprio punto di riferimento.]

Altri suggerimenti

Non dimenticare che spostare un valore in virgola mobile da un registro FPU ad un registro intero richiede una scrittura in RAM seguita da una lettura.

Con floating point code, sarà sempre meglio guardare il quadro più ampio:

Some floating point code
Get sign of floating point value
Some more floating point code

Nello scenario precedente, utilizzando la FPU per determinare il segno sarebbe più veloce, come non ci sarà una lettura / scrittura testa 1 . Il processore Intel FPU può fare:

FLDZ
FCOMP

, che imposta i flag di codice condizione per > 0, < 0 e == 0 e può essere utilizzato con FCMOVcc.

Inlining quanto sopra in codice FPU ben scritto sarà battere qualsiasi manipolazioni bit integer e non perderà la precisione 2 .

Note:

  1. Il processore Intel IA32 ha un'ottimizzazione lettura dopo-scrittura in cui non aspetterà i dati da impegnare in RAM / cache, ma basta usare direttamente il valore. E 'invalida ancora la cache anche se così c'è un effetto a catena.
  2. Il processore Intel FPU è 80bits internamente, galleggianti sono 32 e doppie 64, in modo da convertire galleggiare / doppia per ricaricare come intero perderà alcuni bit di precisione. Questi sono pezzi importanti come siete alla ricerca di transizioni intorno 0.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top