2014-09-20 17 views
5

Qualcuno può spiegare cosa fa questa funzione?Come funziona questa funzione di arrotondamento matematico?

static inline void round_to_zero(volatile float *f) 
{ 
    *f += 1e-18; 
    *f -= 1e-18; 
} 

Intendo a parte aggiungere 1e-18 e sottrarlo di nuovo, lo capisco. Ma non capisco quale effetto avrà su un galleggiante passato ad esso. La ragione per cui sto cercando di capirlo è che sto usando il doppio in qualche codice che usa questa funzione (che ho convertito da float). Il suo codice audio, e la funzione di cui sopra proviene da questa biblioteca:

https://github.com/swh/lv2/blob/master/include/ladspa-util.h

Mi chiedo se funzionerà su un doppio come è, o deve essere modificato per la precisione in più una doppia dispone. Sospetto che questo blocchi gli ultimi bit di dati, cancellandoli dal float se ci sono, anche se non capisco come. Ma immagino che se questo è ciò che fa, dovrò cambiare l'esponente per adattarlo al doppio.

TIA, Pete

+0

Sembra che questo sia un codice fortemente ottimizzato che si basa sulla gestione IEEE754 dei numeri denormali con il tipo di dati previsto che il codice sta gestendo (http://en.wikipedia.org/wiki/Denormal_number). Oltre a ciò non posso dare un senso a ciò. – caskey

+0

I numeri denormali di @caskey 'float' sono più piccoli di questo. – ouah

risposta

1

Il codice seguente illustra ciò che la funzione fa.

int main(void) 
{ 
    float a; 

    a = -1.0; 
    a /= 1e100; 
    printf("%f\n", a); 

    round_to_zero(&a); 
    printf("%f\n", a); 
} 

La cosa che dovete sapere è che IEEE-754 numeri in virgola mobile hanno due possibili valori per 0. C'è uno positive 0 e uno negative 0. La funzione round_to_zero converte il negativo 0 in positivo 0.

Il valore 1e-18 è circa 1 lsb per il numero a precisione doppia 1.0. Quindi non penso che siano necessarie modifiche per usare quella funzione con double (oltre a cambiare il tipo di argomento, ovviamente).

+0

Grazie - puoi spiegare come funziona? Perché l'aggiunta di 1e-18 e la sottrazione hanno questo effetto, mi chiedo quale effetto abbia sull'intera gamma di numeri che potrebbero essere passati. Se il valore 1e-18 è 1 lsb per un double, deve essere molto inferiore a questo per un float, per il quale è stato progettato il codice, quindi mi chiedo se la funzione abbia un effetto diverso sul float rispetto al double? Dal momento che non lo capisco davvero (anche se capisco la necessità di rimuovere il segno), sono sospettoso di sottili differenze che potrebbero rendere il codice che usa il doppio eseguono in modo diverso (non rilevato). – Pete

+0

@Pete - L'idea è che se inizi con 0 negativo, quindi l'aggiunta di 1e-18 ti dà un piccolo numero positivo, e sottrai 1e-18 ti dà un 0. positivo Se inizi con qualsiasi altro numero, aggiungendo e sottraendo 1e-18 non ha alcun effetto netto sul numero. Questa sembra essere la teoria comunque. – user3386109

1

Pensavo di dover tornare su questo per aggiungere i seguenti dettagli.

Mentre la risposta che si riferisce alla conversione di uno zero negativo in positivo è vera e mi è stata utile, c'è di più.

L'aggiunta di 1e-18 e quindi la sottrazione da un float annulla effettivamente numeri molto bassi dal float. Questo è usato nelle applicazioni audio perché i filtri possono ricircolare i piccoli galleggianti attraverso funzioni che dividono continuamente i galleggianti, risultando in un numero sempre più piccolo. Una volta che il numero diventa denormalizzato (come menzionato da Caskey), la velocità di elaborazione per quel numero in molti cpu (x86 incluso) diventa fino a 100 volte più lento.

Aggiungendo un numero molto più grande del numero di dimensione di denormale per quel tipo di dati, si cancella il piccolo valore memorizzato nel tipo. Sottraendo lo stesso valore maggiore si ottiene il tipo con zero, che non influisce sulla velocità di elaborazione se elaborato. La ragione per cui cancelli il piccolo valore è che la precisione Significand del tipo non è abbastanza grande da contenere sia il valore molto piccolo, sia il valore più grande che hai appena aggiunto.

Ad esempio:

Inizia con un campione audio con un valore di 1.0f.

Mettere questo attraverso una funzione 40 volte che divide per 10, risultante in un valore di 1e-40.

v = 0.0100000 e-38 (il tipo float ha circa 8 decimali di precisione e un esponente fino a 38, quindi guarda in memoria come l'ho scritto qui).

Questo è ora un valore denormale per un tipo float e farà sì che una CPU lo elabori molto lentamente. Come sbarazzarsi del rallentamento? Rendilo zero. Quindi:

Aggiungi 1e-18; risultato: 1.00000000 e-18 (notare che l'originale 1e-40 è troppo piccolo per essere rappresentato nel significato a 8 cifre e se è già in possesso del valore 1e-18 molto più grande).

Poi sottrarre il valore 1e-18: 0,00 milioni e-0

Perciò abbiamo produrre zero, cancellando il valore denormal originale, e le nostre CPU ci ringrazia.

Problemi correlati