Jeśli w praktyce obliczamy wartości funkcji sinus lub cosinus dla danego kąta, to bardzo często zdarza się, że tak naprawdę potrzebujemy ich obu. Jest tak przy obliczaniu punktów okręgu, przy rozkładzie wektorów sił i jeszcze dla wielu innych okoliczności. Zależy nam naturalnie, aby policzyć to wszystko tak szybko, jak tylko się da, dlatego dobrze jest stosować funkcje w rodzaju sincos, które wyznaczają obie wartości jednocześnie.
Niestety nie każdy język programowania taką funkcję posiada. Mają ją języki shaderowe (GLSL, HLSL i asembler GPU) oraz np. Delphi, ale już nasz ulubiony C++ nie. Można by oczywiście uzupełnić ten brak poprzez taką implementację:
ale chyba nie trzeba nikogo przekonywać, że większego sensu ona nie ma :) Nie występuje tu bowiem żaden zysk na wydajności, bo wartości są obliczane oddzielnie.
Co więc można zrobić? Ano wykorzystać to, co drzemie w jednostce zmiennoprzecinkowej procesora, ale nie jest używane przez wszystkie języki wyższego poziomu. Istnieje mianowicie instrukcja FSINCOS, która wykonuje całą potrzebną “magię”. Należy ją tylko opakować:
Jakkolwiek tajemniczo to może wyglądać, funkcja ta po prostu ładuje argument (kąt) na stos FPU, by potem odebrać wyniki – cosinus i sinus. W przypadku operowania na liczbach typu float
nie ma możliwości podania zbyt dużego/małego argumentu, więc nie trzeba sprawdzać rejestru flag.
I tyle – funkcja mała, acz użyteczna. Asembler czasem się przydaje, proszę państwa ;P
a nie lepiej wrzucić sobie wynik funkcji do tablicy na początku programu i potem tylko pobierać przez jakieś makro?
@q: no jeśli wiesz dla jakiego konkretnie kąta będą Ci potrzebne te wartości to pewnie że łatwiej i szybciej, ale weź pod uwagę, że kąt może zmieniać się dynamicznie. Musiałbyś wtedy stworzyć całą tablicę wartości.
Zresztą gdzieś już o tym pisałem, można stworzyć 2 tablice np 3600 elementów typu float i powpisywać do nich wartości sin i cos. Można też większe dla zwiększenia precyzji, ale to chyba nie jest najlepszy pomysł mimo wszystko ^^
Ciekawe czy ten kod będzie działał pod g++ w linuxie…
Xion: dzięki za kodzik, przyda się ;)
moriturius: AFAIK nie będzie, bo GCC ma inną składnię “embeddowanego” asm’a.
@Queight: wiem juz wiem ^^. Wlasnie kombinuje jak to przepisac na GCC ;) Mam tylko problem z wywolaniami fstp i tym podobnych.
@moriturius: gcc i glibc już to mają :P
q: Funkcja sincos() to optymalizacja z gatunku niewielkich, ale za to pewnych i bezproblemowych. Tablicowanie może dać większe zyski, ale i większe problemy, chociażby z dokładnością. Chyba w GPG2 jest ładnie napisane o tablicowaniu sinusów.
@agent_J: gdzie? ^^
http://linux.die.net/man/3/sincos
@agent_J: dzieki ^^ w sumie to moglem sie domyslic ze bedzie w math.h… ale ok – pozno juz :P
Tylko że teraz rozkaz fsincos jest trzymany tylko dla zachowania kompatybilności. Biblioteki Intela liczą to na SSE bo jest szybciej :)
Ale za to FPU jest na większości nawet najbardziej archaicznych komputerów, które dotrwały do naszych czasów :)
Xion: których i tak nikt nie używa (nerdy się nie liczą ;))