Магическое значение, очевидно - пи/2
Вид для печати
У каждой функции есть свои ограничения, здесь простота и маленький цикл программы, https://owen.ru/forum/showthread.php...l=1#post397789
В отличие от рядов Тейлора, в нескольких функциях, с огромным циклом программы (порядка 72 мс).
изначально некоторые функции использовались в астротаймере, где углы ограничены. https://owen.ru/forum/showthread.php...696#post448696
Взял у kondor3000 по ссылке https://owen.ru/forum/showthread.php...l=1#post397789
У меня ощущение, что по алгоритму аргумент последовательно приводится к диапазону 0...2Pi, 0...Pi, 0...Pi/2, а потом по ряду Тейлора (Маклорена) вычисляется значение для фиксированного количества (пяти) слагаемых с изменением последовательности сложения для повышения точности.
Как отмечалось выше, для значений Pi/2 (90 градусов) и для (3/4)Pi (270 градусов) функция возвращает ошибочное значение 0,0 вместо точного 1,0.
Как отмечал выше kondor3000, функция предназначалась для конкретной задачи с узким диапазоном аргумента и ошибка не проявлялась.
Скрытый текст:
Пробовал вычислить sin разными способами:
просто ряд Тейлора (Макларена) - с ограничением количества слагаемых количеством от 11 до 15:
Пересчёт аргумента (неоднократное снижение в 2 раза) до приведения к диапазону (-1,5...+1,5), вычисление sin и cos при помощи ряда Тейлора, пересчёт результатов к исходному значению аргумента
Формула удвоения аргумента
sin(2x) = 2 sin(x) cos(x)
cos(2x) = 1 - [sin(x)]^2
пересчёт аргумента к меньшему числу, вычисление рядом Тейлора (Маклорена), пересчёт результата:
По аналогии с предыдущим способом, но по готовой формуле
sin(5x) = 16 [sin(x)]^5 - 20 [sin(x)]^3 + 5 sin(x)
деление аргумента на 5, вычисление sin при помощи ряда Тейлора (Маклорена), пересчёт результата по формуле:
Аналогично, но аргумент делится на 7
sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x)
деление аргумента на 7, вычисление sin при помощи ряда Тейлора (Маклорена), пересчёт результата по формуле:
Вычисление синуса через ряд Маклорена (Тейлора), дополненное суммированием при помощи алгоритма Кэхэна (Kahan)
вычисление при помощи ряда Тейлора (Маклорена), суммирование ряда по алгоритму Кэхэна:
Ещё пробовал выполнить приведение исходного аргумента к (0...Pi/2) или (-Pi/4...+Pi/4), но столкнулся с тем, что константы, кратные Pi, которые использую при этих приведениях, тоже имеют конечную точность представления в типе REAL и во время вычислений так же теряю точность, как если бы не выполнял их.
Поэтому и стал искать другие способы...
Наверное, остановлю изыскания, т.к. пока новых идей нет.
Для проверки использовал программу, которая по переключению от дискретного входа меняет аргумент с (-0,227) на его эквивалент (-0,227 + 2Pi)
По итогам моделирования приведу сводную таблицу для четырёх значений x, x+2*Pi, abs(x), abs(x+2*Pi)
Думаю, что все ухищрения с уменьшением аргумента не приводят к повышению точности из-за сложения несравнимых слагаемых при обратном пересчёте результата.Код:x = -0.2270000
6.0561857
0.2270000
6.5101852
точное = -0.2250555
-0.2250555
0.2250555
0.2250555
SIN_RAD = -0.2250555
-0.2250550
0.2250555
0.2250555
sin_1_ = -0.2250548 - ряд Тейлора
-0.2250548
0.2250555
0.2250553
sin_2_ = -0.2250551 - пересчёт аргумента, ряд Тейлора, пересчёт результата
-0.2250551
0.2250555
0.2250553
sin_3_ = -0.2250549 - пересчёт аргумента деление на 5, формула sin(5x)
-0.2250549
0.2250555
0.2250553
sin_4_ = -0.2250543 - пересчёт аргумента деление на 7, формула sin(7x)
-0.2250543
0.2250555
0.2250552
sin_5_ = -0.2250551 - ряд Тейлора с алгоритмом сложения Кэхэна
-0.2250551
0.2250555
0.2250553
Например, для sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x) видно, что в наихудшем случае происходит сложение чисел, сопоставимых с 112, что ухудшает точность в 7 знаке после запятой.
Для варианта с несколькими делениями на 2, происходят операции с числами, сопоставимыми с 1.
Ряд Тейлора с алгоритмом сложения Кэхэна не дал ожидаемых результатов из-за сложения сильно несопоставимых чисел - порядка 36 и 1 - разряды 6 и 7 после запятой сразу содержали мусор и мусором остались. Из-за учёта мелких слагаемых алгоритмом Кэхэна результат немного отличается от обычного Тейлора.
Вычисления алгоритмом SIN_RAD, возможно, более точны на диапазоне 0...Pi/2, но, не считая ошибок вычисления в двух точках (90 и 270 градусов), на всём диапазоне аргумента вычисляет с сопоставимой погрешностью - 6 знаков после запятой, против 5-6 у других методов - что не удивительно, ведь они все основаны на вычислении ряда Тейлора.
Для себя сделал выводы что из перебранных алгоритмов
- в целом результаты сопоставимы
- предпочту использовать или просто ряд Тейлора или с несколькими делениями аргумента на 2 с последующим пересчётом результата - за счёт простоты и скорости выполнения
Мне показалось, что этот алгоритм даёт не совсем точный результат для x=-0.227+2*Pi, т.е. не полного оборота. Именно поэтому и решил сказать о сопоставимости точности.
Результаты эксперимента для четырёх точек
Справедливости ради, стоит отметить, что из-за ограничений разрядной сетки и погрешности при сложении с константой 2*Pi результат для полученного аргументаКод:x = -0.2270000
6.0561857 = -0.227 + 2*Pi
0.2270000 = |-0.227|
6.5101852 = |-0.227| + 2*Pi
точное = -0.2250555
-0.2250555
0.2250555
0.2250555
SIN_RAD = -0.2250555
-0.2250550
0.2250555
0.2250555
sin(6.0561857 = -0.227 + 2*Pi)=-0,22505512, а не 0.22505550
Т.е. в последнем 7 разряде несовпадение с точным значением. Поэтому эксперимент показал, что точность не превышает 6 знаков.
Я не проверил, вполне возможно, на диапазоне 0...Pi/2 точность может быть выше. Т.к. для повышения точности при сложении переставлен порядок слагаемых.
Просто представьте, что для x=1.5 первое слагаемое будет 1,5, что сразу "убивает" надежду на 7 разряд после запятой.
SIN_Rad:=((pow(b,9)/362880 + (((b-(pow(b,3)/6)) +pow(b,5)/120)- pow(b,7)/5040))-pow(b,11)/39916800)*Zn ;
Скорость вычисления я тоже не проверял. Всё зависит от реализации компилятора.
Мне кажется, что 5 вызовов сторонней функции само по себе должно привнести затраты времени.
Далее, вероятно, функция pow(a, b) = exp(b*log(a)) с учётом знака - я просто не знаю, как иначе вычислить a^b с вещественными числами. Т.е. довольно затратные операции.
В моих реализациях есть циклы, это не прибавляет скорости, тоже накладные расходы.
Т.е. при сравнимой точности 5-6 знаков после запятой и скорости могут быть сопоставимыми - и делаю вывод о приблизительной равноценности реализации функции.
Видел код Син. с использованием нескольких табличных значений арктангннса. Правда не на ST. Может адаптировать (портировать) его?
Честно, не знаю, насколько будет быстрее работать?
Можно попробовать...
Мне просто не нравится код с неподходящим алгоритмом, особенно опубликованный в примерах - самый яркий это st функция для Pt1000 из компонентов.
При расчётах вызывается функция вычисления полинома (уже странно!)
Смотрим содержимое функцииКод:............
f_PT1000:=f_Pol4((RtR0-1),4,0,D1,D2,D3,D4); //вызов функции "f_pol4"
.............
И это вместо простого вычисления по схеме ГорнераКод:///<Description>Функция вычисления полинома четвертой степени</Description>
///<OutputDescription>Результат вычисления</OutputDescription>
///<Author>ОВЕН</Author>
///<GroupName>Аналоговые преобразования</GroupName>
function f_Pol4: Real; //функция вычисления полинома 4й степени
var_input
///<Description>Заданный корень</Description>
X : Real;
///<Description>Степень</Description>
exp: udint;
///<Description>Коэффициент D0</Description>
D0: Real;
///<Description>Коэффициент D1</Description>
D1: Real;
///<Description>Коэффициент D2</Description>
D2: Real;
///<Description>Коэффициент D3</Description>
D3: Real;
///<Description>Коэффициент D4</Description>
D4: Real;
end_var
var
i: udint;
d: array [0..4] of Real;
end_var
f_Pol4:=0;
d[0]:=d0;
d[1]:=d1;
d[2]:=d2;
d[3]:=d3;
d[4]:=d4;
for i := 0 to exp do
f_Pol4:=f_Pol4 + d[i]*pow(X,udint_to_real(i));
end_for
end_function
Т.е. простое действие обёрнуто в функцию, содержащую вместо нескольких арифметических действий:Код:f_PT1000 := RtR0 * (D1 + RtR0 * (D2 + RtR0 * (D3 + RtR0 * D4)));
- перепаковку входных данных в массив
- цикл, который из-за малого числа итераций можно развернуть
- вызов затратной функции pow(a, b)=exp(b*ln(a)) вместо одного умножения
А ведь на этот код Алиса смотрит! Стыдно же!
Кстати, при последовательном вычислении членов ряда ошибка не возникает. Значение функций SIN_Rad и SIN_Rad2 практически совпадают. По данным fSUB разность 5.96E-8 отмечается лишь на некоторых углах (+/- 50, 245, 255 … - проверял через 5 градусов)). Извиняюсь за грубое редактирование (только для примера).Скрытый текст: