Gostaria de calcular o seno e o cosseno de um valor juntos (por exemplo, para criar uma matriz de rotação). Claro que eu poderia computá-los separadamente um após o outro a = cos(x); b = sin(x);
, mas gostaria de saber se existe uma maneira mais rápida quando precisar dos dois valores.
Edit: Para resumir as respostas até agora:
Vlad disse que existe o comando asm
FSINCOS
computando os dois (quase ao mesmo tempo que uma chamada paraFSIN
sozinho)Como Chi notou, esta otimização às vezes já é feita pelo compilador (ao usar sinalizadores de otimização).
caf apontou, que funções
sincos
esincosf
provavelmente estão disponíveis e podem ser chamadas diretamente apenas incluindomath.h
A abordagem de tanascius de usar uma tabela de consulta é discutida como controversa. (No entanto, no meu computador e em um cenário de benchmark, ele é executado 3x mais rápido do que
sincos
com quase a mesma precisão para pontos flutuantes de 32 bits.)Joel Goodwin vinculou a uma abordagem interessante de uma técnica de aproximação extremamente rápida com uma precisão muito boa (para mim, isso é ainda mais rápido do que a consulta à tabela)
sinx ~ x-x^3/6
ecosx~1-x^2/4
como aproximações se você se preocupa mais com a velocidade do que com a precisão. Você pode adicionar termos em qualquer uma das séries à medida que coloca mais peso na precisão ( en.wikipedia.org/wiki/Taylor_series role para baixo para trig taylor series.) Observe que esta é uma maneira geral de aproximar qualquer função desejada emn
tempos diferenciáveis . Portanto, se você tiver alguma função maior à qual os senos e cossenos pertencem, você obterá uma velocidade muito maior se a aproximar em vez de sin, cos independentemente.x
próximos a algum pontox_0
, então expanda sua série de Taylor ao redor emx_0
vez de 0. Isso lhe dará excelente precisão perto,x_0
mas quanto mais longe você pioram os resultados. Você provavelmente pensou que a precisão era péssima quando olhou para a resposta fornecida e tentou valores distantes de0
. Essa resposta é com sin, cos expandido em torno de 0.Respostas:
Os processadores Intel / AMD modernos possuem instruções
FSINCOS
para calcular as funções seno e cosseno simultaneamente. Se você precisa de uma otimização forte, talvez deva usá-la.Aqui está um pequeno exemplo: http://home.broadpark.no/~alein/fsincos.html
Aqui está outro exemplo (para MSVC): http://www.codeguru.com/forum/showthread.php?t=328669
Aqui está mais um exemplo (com gcc): http://www.allegro.cc/forums/thread/588470
Espero que um deles ajude. (Eu não usei esta instrução, desculpe.)
Como eles são suportados no nível do processador, espero que sejam muito mais rápidos do que as pesquisas de tabela.
Edit:
Wikipedia sugere que
FSINCOS
foi adicionado 387 processadores, então você dificilmente pode encontrar um processador que não o suporte.Edit:
a documentação da Intel afirma que
FSINCOS
é cerca de 5 vezes mais lento do queFDIV
(isto é, divisão de ponto flutuante).Editar:
Observe que nem todos os compiladores modernos otimizam o cálculo de seno e cosseno em uma chamada para
FSINCOS
. Em particular, meu VS 2008 não fazia isso.Edit:
O primeiro link de exemplo está morto, mas ainda há uma versão na Wayback Machine .
fonte
fsincos
instrução não é "muito rápida". O manual de otimização da própria Intel cita que exige entre 119 e 250 ciclos em micro-arquiteturas recentes. A biblioteca matemática da Intel (distribuída com ICC), por comparação, pode calcular separadamentesin
ecos
em menos de 100 ciclos, usando uma implementação de software que usa SSE em vez da unidade x87. Uma implementação de software semelhante que calculasse os dois simultaneamente poderia ser ainda mais rápida.sin
computação embutida para eles tirarem vantagem, entretanto; eles usam as mesmas instruções SSE que todos os outros. Para seu segundo comentário, a velocidade relativa afdiv
é irrelevante; se houver duas maneiras de fazer algo e uma for duas vezes mais rápida que a outra, não faz sentido chamar a mais lenta de "rápida", independentemente de quanto tempo leva em relação a alguma tarefa completamente não relacionada.sin
função de software em sua biblioteca oferece precisão total de dupla precisão. Afsincos
instrução oferece um pouco mais de precisão (dupla estendida), mas essa precisão extra é jogada fora na maioria dos programas que chamam asin
função, pois seu resultado é geralmente arredondado para precisão dupla por operações aritméticas posteriores ou um armazenamento na memória. Na maioria das situações, eles oferecem a mesma precisão para uso prático.fsincos
não é uma implementação completa por si só; você precisa de uma etapa de redução de intervalo adicional para colocar o argumento no intervalo de entrada válido para afsincos
instrução. A bibliotecasin
e ascos
funções incluem essa redução, bem como a computação principal, de modo que são ainda mais rápidos (em comparação) do que os tempos de ciclo que listei podem indicar.Os processadores x86 modernos têm uma instrução fsincos que fará exatamente o que você está pedindo - calcular sen e cos ao mesmo tempo. Um bom compilador de otimização deve detectar o código que calcula sen e cos para o mesmo valor e usar o comando fsincos para executá-lo.
Demorou alguns ajustes de sinalizadores do compilador para que isso funcionasse, mas:
Tada, use a instrução fsincos!
fonte
-ffast-math
e-mfpmath
conduza a resultados diferentes em alguns casos.fsin
efcos
. :-(__CIsin
e__CIcos
.Quando precisar de desempenho, você pode usar uma tabela sin / cos pré-calculada (uma tabela servirá, armazenada como um Dicionário). Bem, depende da precisão que você precisa (talvez a mesa seja muito grande), mas deve ser muito rápido.
fonte
sin
porque a tabela pré-computada irá destruir o cache.Tecnicamente, você conseguiria isso usando números complexos e a Fórmula de Euler . Assim, algo como (C ++)
deve fornecer seno e cosseno em uma única etapa. Como isso é feito internamente é uma questão do compilador e da biblioteca em uso. Pode (e pode) levar mais tempo para fazer isso dessa forma (só porque a Fórmula de Euler é usada principalmente para calcular o complexo
exp
usandosin
ecos
- e não o contrário), mas pode haver alguma otimização teórica possível.Editar
Os cabeçalhos no
<complex>
GNU C ++ 4.2 estão usando cálculos explícitos desin
ecos
dentropolar
, então não parece muito bom para otimizações lá, a menos que o compilador faça alguma mágica (veja as opções-ffast-math
e-mfpmath
conforme escritas na resposta de Chi ).fonte
Você pode calcular qualquer um e usar a identidade:
mas, como diz @tanascius, uma mesa pré-computada é o caminho a percorrer.
fonte
sqrt()
é frequentemente otimizado em hardware, por isso pode muito bem ser mais rápido quesin()
oucos()
. O poder é apenas auto-multiplicação, então não usepow()
. Existem alguns truques para obter raízes quadradas razoavelmente precisas muito rapidamente sem suporte de hardware. Por fim, certifique-se de criar um perfil antes de fazer qualquer um desses.Se você usa a biblioteca GNU C, pode fazer:
e você terá declarações dos
sincos()
,sincosf()
esincosl()
funções que calculam os dois valores juntos - presumivelmente no caminho mais rápido para sua arquitetura alvo.fonte
Há coisas muito interessantes nesta página do fórum, que se concentra em encontrar boas aproximações que sejam rápidas: http://www.devmaster.net/forums/showthread.php?t=5784
Aviso: Não usei nada disso sozinho.
Atualização de 22 de fevereiro de 2018: Wayback Machine é a única maneira de visitar a página original agora: https://web.archive.org/web/20130927121234/http://devmaster.net/posts/9648/fast-and-accurate- seno-cosseno
fonte
Muitas bibliotecas de matemática C, como indica caf, já têm sincos (). A exceção notável é o MSVC.
E com relação à pesquisa, Eric S. Raymond em Art of Unix Programming (2004) (Capítulo 12) diz explicitamente que isso é uma má ideia (no momento presente):
Mas, a julgar pela discussão acima, nem todos concordam.
fonte
fsincos
(instrução da CPU!) Uma tentativa para os outros. Freqüentemente, é tão rápido quanto interpolar sen e cos de uma grande mesa.Não acredito que as tabelas de pesquisa sejam necessariamente uma boa ideia para esse problema. A menos que seus requisitos de precisão sejam muito baixos, a mesa precisa ser muito grande. E as CPUs modernas podem fazer muitos cálculos enquanto um valor é buscado na memória principal. Esta não é uma daquelas questões que podem ser respondidas adequadamente por argumentos (nem mesmo os meus), teste, meça e considere os dados.
Mas eu observaria as implementações rápidas de SinCos que você encontra em bibliotecas como ACML da AMD e MKL da Intel.
fonte
Se você deseja usar um produto comercial e está calculando vários cálculos sin / cos ao mesmo tempo (para que possa usar funções vetorizadas), consulte a Biblioteca de Kernel de Matemática da Intel.
Tem uma função sincos
De acordo com essa documentação, a média é de 13,08 relógios / elemento no core 2 duo no modo de alta precisão, o que eu acho que será ainda mais rápido que o fsincos.
fonte
vvsincos
ouvvsincosf
do Accelerate.framework. Acredito que a AMD também tenha funções semelhantes em sua biblioteca vetorial.Este artigo mostra como construir um algoritmo parabólico que gera o seno e o cosseno:
Truque DSP: Aproximação Parabólica Simultânea de Sin e Cos
http://www.dspguru.com/dsp/tricks/parabolic-approximation-of-sin-and-cos
fonte
Quando o desempenho é crítico para esse tipo de coisa, não é incomum introduzir uma tabela de pesquisa.
fonte
Para uma abordagem criativa, que tal expandir a série Taylor? Como eles têm termos semelhantes, você poderia fazer algo como o seguinte pseudo:
Isso significa que você faz algo assim: começando em x e 1 para sen e cosseno, siga o padrão - subtraia x ^ 2/2! do cosseno, subtraia x ^ 3/3! do seno, adicione x ^ 4/4! ao cosseno, adicione x ^ 5/5! para seno ...
Não tenho ideia se isso seria um bom desempenho. Se você precisar de menos precisão do que o sin () e o cos () integrados fornecem, pode ser uma opção.
fonte
Há uma boa solução na biblioteca CEPHES que pode ser bem rápida e você pode adicionar / remover precisão de forma bastante flexível por um pouco mais / menos tempo de CPU.
Lembre-se de que cos (x) e sin (x) são as partes reais e imaginárias de exp (ix). Portanto, queremos calcular exp (ix) para obter ambos. Pré-calculamos exp (iy) para alguns valores discretos de y entre 0 e 2pi. Mudamos x para o intervalo [0, 2pi). Em seguida, selecionamos y que está mais próximo de x e escrevemos
exp (ix) = exp (iy + (ix-iy)) = exp (iy) exp (i (xy)).
Obtemos exp (iy) da tabela de pesquisa. E desde | xy | for pequeno (no máximo metade da distância entre os valores de y), a série de Taylor convergirá bem em apenas alguns termos, então usamos isso para exp (i (xy)). E então precisamos apenas de uma multiplicação complexa para obter exp (ix).
Outra propriedade interessante disso é que você pode vetorizá-lo usando SSE.
fonte
Você pode querer dar uma olhada em http://gruntthepeon.free.fr/ssemath/ , que oferece uma implementação vetorizada SSE inspirada na biblioteca CEPHES. Tem boa precisão (desvio máximo de sin / cos na ordem de 5e-8) e velocidade (supera ligeiramente fsincos em uma base de chamada única e um vencedor claro sobre vários valores).
fonte
Eu postei uma solução envolvendo montagem ARM em linha capaz de calcular o seno e o cosseno de dois ângulos de uma vez aqui: Fast seno / cosseno para ARMv7 + NEON
fonte
Uma aproximação precisa, porém rápida, da função sin e cos simultaneamente, em javascript, pode ser encontrada aqui: http://danisraelmalta.github.io/Fmath/ (facilmente importado para c / c ++)
fonte
Você já pensou em declarar tabelas de pesquisa para as duas funções? Você ainda teria que "calcular" sin (x) e cos (x), mas seria decididamente mais rápido, se você não precisar de um alto grau de precisão.
fonte
O compilador MSVC pode usar as funções SSE2 (internas)
em compilações otimizadas se os sinalizadores de compilador apropriados forem especificados (no mínimo / O2 / arch: SSE2 / fp: rápido). Os nomes dessas funções parecem implicar que elas não calculam sen e cos separados, mas ambos "em uma única etapa".
Por exemplo:
Montagem (para x86) com / fp: rápido:
Montagem (para x86) sem / fp: rápido, mas com / fp: preciso em vez (que é o padrão) chama sen e cos separados:
Portanto, / fp: fast é obrigatório para a otimização do sincos.
Mas observe que
talvez não seja tão preciso quanto
devido à falta de "preciso" no final de seu nome.
No meu sistema "ligeiramente" mais antigo (Intel Core 2 Duo E6750) com o compilador MSVC 2019 mais recente e otimizações apropriadas, meu benchmark mostra que a chamada sincos é cerca de 2,4 vezes mais rápida do que chamadas sin e cos separadas.
fonte