Вычисление синуса и Косинуса в одном кадре
у меня есть научный код, который использует как синус, так и косинус одного и того же аргумента (мне в основном нужна сложная экспонента этого аргумента). Мне было интересно, можно ли сделать это быстрее, чем вызывать синусоидальные и косинусные функции отдельно.
также мне нужна только точность 0.1%. Итак, есть ли способ найти триггерные функции по умолчанию и усечь серию мощности для скорости?
одна вещь, которую я имею в виду, есть ли способ, чтобы выполнить операция остатка такая, что результат всегда положительный? В моем собственном алгоритме я использовал x=fmod(x,2*pi);
но тогда мне нужно будет добавить 2pi, если x отрицательный (меньший домен означает, что я могу использовать более короткий ряд мощности)
EDIT: LUT оказался лучшим подходом для этого, однако я рад, что узнал о других методах приближения. Я также посоветую использовать явное приближение средней точки. Вот что я в итоге сделал:--5-->
const int N = 10000;//about 3e-4 error for 1000//3e-5 for 10 000//3e-6 for 100 000
double *cs = new double[N];
double *sn = new double[N];
for(int i =0;i<N;i++){
double A= (i+0.5)*2*pi/N;
cs[i]=cos(A);
sn[i]=sin(A);
}
следующая часть приближается (медианы) синусно-косинусный(2*Пи*(wс2+Т[Дж]*(cotp*Т[Дж]-туалет)))
double A=(wc2+t[j]*(cotp*t[j]-wc));
int B =(int)N*(A-floor(A));
re += cs[B]*f[j];
im += sn[B]*f[j];
другой подход мог бы использовать разложение Чебышева. Для поиска коэффициентов можно использовать свойство ортогональности. Оптимизированный для экспоненциального, он выглядит так:
double fastsin(double x){
x=x-floor(x/2/pi)*2*pi-pi;//this line can be improved, both inside this
//function and before you input it into the function
double x2 = x*x;
return (((0.00015025063885163012*x2-
0.008034350857376128)*x2+ 0.1659789684145034)*x2-0.9995812174943602)*x;} //7th order chebyshev approx
5 ответов
Если вы ищете быструю оценку с хорошей (но не высокой) точностью с powerseries, вы должны использовать разложение в полиномах Чебышева: табулируйте коэффициенты (вам понадобится очень мало для точности 0,1%) и оцените разложение с рекурсионными отношениями для этих полиномов (это действительно очень просто).
ссылки:
- Табулированных коэффициентов: http://www.ams.org/mcom/1980-34-149/S0025-5718-1980-0551302-5/S0025-5718-1980-0551302-5.pdf
- оценка расширения Чебышева:https://en.wikipedia.org/wiki/Chebyshev_polynomials
вам нужно (a) получить аргумент "reduced" в диапазоне-pi/2..+pi / 2 и, следовательно, затем (b) обрабатывать знак в ваших результатах, когда аргумент фактически должен был быть в "другой" половине полного элементарного интервала-pi..+пи. Эти аспекты не должны создавать серьезной проблемы:
- определите (и" запомните " как целое число 1 или -1) знак в исходном угле и продолжите с абсолютным значением.
- используйте функцию по модулю, чтобы уменьшить до интервала 0..2Пи
- определите (и" запомните "как целое число 1 или -1), находится ли оно во" второй " половине, и если да, вычитайте pi*3/2, иначе вычитайте pi/2. Примечание: это эффективно взаимозаменяет синус и косинус (кроме знаков); примите это в учет в итоговой оценке.
Это завершает шаг, чтобы получить угол в-pi / 2..+pi / 2 После оценки синуса и Косинуса с помощью разложений Хеба примените "флаги" шагов 1 и 3 выше, чтобы получить правильные знаки в значениях.
просто создайте таблицу поиска. Следующие позволит вам найти Sin и cos любого значения Радиан от-2Пи и 2Пи.
// LOOK UP TABLE
var LUT_SIN_COS = [];
var N = 14400;
var HALF_N = N >> 1;
var STEP = 4 * Math.PI / N;
var INV_STEP = 1 / STEP;
// BUILD LUT
for(var i=0, r = -2*Math.PI; i < N; i++, r += STEP) {
LUT_SIN_COS[2*i] = Math.sin(r);
LUT_SIN_COS[2*i + 1] = Math.cos(r);
}
вы индексируете в таблицу поиска по:
var index = ((r * INV_STEP) + HALF_N) << 1;
var sin = LUT_SIN_COS[index];
var cos = LUT_SIN_COS[index + 1];
вот скрипка, которая отображает ошибку%, которую вы можете ожидать от разных размеров LUTS http://jsfiddle.net/77h6tvhj/
редактировать вот ideone (c++) с ~benchmark~ против float sin и cos. http://ideone.com/SGrFVG для любого ориентира на ideone.com стоит лут в 5 раз быстрее.
один из способов пойти было бы узнать, как реализовать CORDIC. Это не сложно и довольно интересно интеллектуально. Это дает вам как Косинус и синус. Википедия дает пример MATLAB это должно быть легко адаптироваться на C++.
заметьте что вы можете увеличить скорость и уменьшить точность просто путем понижать параметр n
.
о вашем втором вопросе, он уже был задан здесь (in C). Кажется, что простого пути нет.
вы также можете рассчитать синус, используя квадратный корень, учитывая угол и косинус.
В приведенном ниже примере предполагается, что угол в диапазоне от 0 до 2π:
double c = cos(angle);
double s = sqrt(1.0-c*c);
if(angle>pi)s=-s;
для поплавков с одной точностью Microsoft использует 11-градусное полиномиальное приближение для синуса, 10-градусное для Косинуса:XMScalarSinCos. Они также имеют более быструю версию, XMScalarSinCosEst,которая использует полиномы более низкой степени.
Если вы не в Windows, вы найдете тот же код + коэффициенты ВКЛ geometrictools.com под лицензией Boost.