Комментарии 28
хотя в других реализациях эти вызовы будут
Скорее всего не во внутреннем цикле — там просто будут использоваться табличные значения.
Кстати насколько разошлись значения синуса и косинуса от реальных на последнем шаге?
Оригинальная статья умалчивает, но я провел собственное расследование: ссылка
Результаты:
BEFORE: Sin = +0.00000000000000000000 (expected 0), Cos = +1.00000000000000000000 (expected 1)
AFTER: Sin = -0.00000000000000035196 (expected 0), Cos = +1.00000000000642160630 (expected 1)
ERROR: Sin = -0.00000000000000035196 (expected 0), Cos = +0.00000000000642160630 (expected 0)
ERROR: Sin = -3.51959e-16 (expected 0), Cos = +6.42161e-12 (expected 0)
То есть, на 1 миллион итераций точность около 10-12 знаков.
Для сравнения:
Точность float (32): 7-8 знаков
Точность double (64): 15-17 знаков
Точность long double (80): 18-19 знаков
Я использовал long double.
Для генератора сигналов на бюджетных МК — вполне годно. Там вытянуть амплитуду можно в аналоговой части
Там нет монотонной зависимости, сначала точность растёт, но всё же с определённого момента начинает падать.
Пользоваться, в принципе, можно. Еще и короче (и шустрее) библиотечных вариантов получается. С фиксированной точкой дружит.
А, например, arm_sin_f32 считает синус по табличке с шагом чуть ли не в 1 градус и кубической интерполяции. К этой библиотеке тоже есть вопросы по точности.
Чем меньше шаг — тем больше самих шагов.
en.wikipedia.org/wiki/Goertzel_algorithm#Power-spectrum_terms
и который вполне себе устойчивый и без накопления ошибок
В русскоязычной литературе нет устоявшегося варианта транскрипции фамилии автора алгоритма. Распространены варианты «Алгоритм Герцеля», «Алгоритм Гертцеля», «Алгоритм Горцеля» и другие.
Ваша же ссылка
Тогда не понятно, почему автор вызывает внутри внешнего цикла:
for( int i=0; i<128; i++ )
{
const float wi = (float)i*(2.0f*3.1415927f/4096.0f);
const float sii = sinf( wi );
const float coi = cosf( wi );
ведь по сути там такой-же трюк подходит
Добавить еще пару действий и получится резонансный фильтр 2го порядка, LP/HP/BP.
Имхо, 128х полосовыми фильтрами можно было бы менее ресурсоемко сделать подобие FFT, хватая по 1 сэмплу.
Вот недавно на хабре была статья, как числа округлять. Сегодня: как применять школьную формулу тригонометрии. Это все сложно. Напишите пожалуйста, как извлекать квадратный корень числа с плавающей точкой в C++. Это будет очень актуально для хабра.
Корень тоже был, см. 0x5F3759DF
Таких констант можно кучу напридумывать для любого показателя степени от -1 до 1, о чем в статье «Магическая константа» 0x5f3759df и говорится.
Посмотрите, как организован внутренний цикл, тот, что выполняется 4096 раз: ни одного вызова функций sin() или cos(), хотя в других реализациях эти вызовы будут.В других реализациях этих вызовов тоже нет — там используются предварительно подсчитанные табличные данные. Более того — трюк с последовательным поворотом вектора для вычисления FFT хорошо известен и в частности используется в книге «Numerical Recipes» (страница 612 в третьем издании).
Статья полезная. Тут рядом где-то статья была, как человек графики для Телеграма оптимизиировал — думаю, ему бы понравилось.
Трюк с тригонометрией