2014-09-20 13 views
5

Czy ktoś może wyjaśnić, co robi ta funkcja?Jak działa ta funkcja zaokrąglania matematyki?

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

Mam na myśli oprócz dodawania 1e-18 i odejmowania go ponownie, rozumiem to. Ale nie rozumiem, jaki wpływ będzie miał na pływak przekazany do niego. Powodem, dla którego próbuję to zrozumieć, jest to, że używam podwójnych w jakimś kodzie, który używa tej funkcji (którą przekonwertowałem z float). Jego kod dźwiękowy, a powyższa funkcja pochodzi z tej biblioteki:

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

Zastanawiam się, czy to będzie działać na podwójne jak jest lub musi być modyfikowany za dodatkową precyzję podwójne ma. Podejrzewam, że to odrzuca ostatnie kilka bitów danych, usuwając je z pływaka, jeśli tam są, chociaż nie do końca rozumiem, jak. Ale wyobrażam sobie, że jeśli tak właśnie jest, będę musiał zmienić wykładnik tak, aby odpowiadał podwójnemu.

TIA, Pete

+0

Wygląda na to, że jest to mocno zoptymalizowany kod, który opiera się na obsłudze liczb denormalnych IEEE754 z przewidywanym typem danych obsługiwanych przez kod (http://en.wikipedia.org/wiki/Denormal_number). Poza tym nie mogę tego zrozumieć. – caskey

+0

@ Dozwolone 'denormalne liczby' float' są mniejsze. – ouah

Odpowiedz

1

Poniższy kod demonstruje, co robi ta funkcja.

int main(void) 
{ 
    float a; 

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

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

Rzecz trzeba wiedzieć jest to, że IEEE-754 liczb zmiennoprzecinkowych mają dwie możliwe wartości dla 0. Jest positive 0 i negative 0. Funkcja round_to_zero konwertuje wartość ujemną 0 na wartość dodatnią 0.

Wartość wynosi około 1 lsb dla podwójnej liczby precyzyjnej 1.0. Nie sądzę więc, aby jakiekolwiek modyfikacje były konieczne do użycia tej funkcji z double (poza zmianą oczywiście typu argumentu).

+0

Dzięki - czy możesz wyjaśnić, jak to działa? Dlaczego dodanie 1e-18 i odejmowanie go ma ten efekt, zastanawiam się, jaki ma wpływ na pełen zakres liczb, które mogą być przekazywane. Jeśli wartość 1e-18 wynosi 1 lsb dla double, to musi być znacznie mniejsza niż dla float, dla którego kod został zaprojektowany, więc zastanawiam się czy funkcja ma inny wpływ na float w porównaniu z double? Ponieważ naprawdę tego nie rozumiem (chociaż rozumiem potrzebę usunięcia znaku), jestem podejrzliwy wobec subtelnych różnic, które mogą sprawić, że kod używający podwójnych wyników będzie różnił się (niewykryty). – Pete

+0

@Pete - Chodzi o to, że jeśli zaczynasz od ujemnej 0, to dodanie 1e-18 daje małą dodatnią liczbę, a odjęcie 1e-18 daje dodatnią 0. Jeśli zaczynasz od dowolnej innej liczby, dodawanie i odejmowanie 1e-18 nie ma wpływu netto na liczbę. To i tak wydaje się teoria. – user3386109

1

Myślałem, że powinienem wrócić do tego, aby dodać następujące szczegóły.

Podczas gdy odpowiedź dotycząca konwersji ujemnego zera na pozytywny jest prawdą i była dla mnie przydatna, jest bardziej do tego.

Dodanie 1e-18, a następnie odjęcie od float, rzeczywiście usuwa bardzo niskie liczby z float. Jest to wykorzystywane w aplikacjach audio, ponieważ filtry mogą recyrkulować małe obiekty za pomocą funkcji, które nieustannie dzielą obiekty pływające, co daje coraz mniejszą liczbę. Gdy liczba zostanie zdenormalizowana (jak wspomniał Caskey), szybkość przetwarzania dla tej liczby w wielu procesorach (włączając x86) staje się do 100 razy wolniejsza.

Dodając znacznie większą liczbę niż denormalny numer rozmiaru dla tego typu danych, wymazuje się niewielką wartość zapisaną w typie. Odejmowanie tej samej większej wartości powoduje, że typ będzie miał zero, co nie będzie miało wpływu na szybkość przetwarzania w przypadku przetworzenia. Powodem, dla którego wymazujesz małą wartość jest to, że precyzja Significand w typie nie jest wystarczająco duża, aby pomieścić zarówno bardzo małą wartość, jak i większą wartość, którą właśnie dodałeś.

Na przykład:

start z próbką audio o wartości 1.0f.

Połóż to przez 40 razy funkcję, która dzieli przez 10, co daje wartość 1e-40.

v = 0,0100000 e-38 (typ zmiennoprzecinkowy ma około 8 dziesiętnych dokładności, a wykładnik do 38, więc wygląda w pamięci tak, jak to napisałem tutaj).

To jest teraz denormalna wartość dla typu zmiennoprzecinkowego i spowoduje, że procesor będzie ją przetwarzał bardzo powoli. Jak pozbyć się spowolnienia? Zrób to zero. A więc:

Dodaj 1e-18; wynik: 1.00000000 e-18 (zauważ, że oryginał 1e-40 jest za mały, aby mógł być reprezentowany w 8-cyfrowym znaczniku i jeśli ma już znacznie większą wartość 1e-18).

odejmujemy wartość 1e-18: 0,00000000 e-0

Stąd możemy produkować zera, ocierając się oryginalną wartość Brak reprezentacji, a nasi cpu dzięki nas.