kondor3000.
Функция SIN_Rad для углов 90, 270 градусов (после пересчета в радианы) вместо верного результата выдает ноль. Одно из «магических» значений аргумента 1,5707964. При изменении последней цифры в любую сторону – все нормально. Может это только у меня. (OWEN Logic 2.6.347.0, на OWEN Logic 1.23 тоже самое)
kondor3000.
Функция SIN_Rad для углов 90, 270 градусов (после пересчета в радианы) вместо верного результата выдает ноль. Одно из «магических» значений аргумента 1,5707964. При изменении последней цифры в любую сторону – все нормально. Может это только у меня. (OWEN Logic 2.6.347.0, на OWEN Logic 1.23 тоже самое)
Взял у 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, функция предназначалась для конкретной задачи с узким диапазоном аргумента и ошибка не проявлялась.
Скрытый текст:
Код:
function EXTRACT: bool; // Функция работает не правильно
var_input
X, N : udint;
end_var
Extract := (SHR(X,N) mod 2) > 0; // Функция работает правильно
// EXTRACT_:=(SHR(X,N) AND 1)=1 ; // Функция работает не правильно
end_function
function SIN_RAD: real ; //имя функции и тип данных выхода
var_input //объявление входных переменных
X: real ; //входная переменная с типом данных bool
end_var
var //объявление локальных переменных
Zn : real ;
Pi : real:=3.1415927 ;
a,b: real ;
end_var
Zn:=pow(-1,bool_to_real(EXTRACT(real_to_udint((abs(X))/Pi),0) XOR not (X>0)));
a:=(abs(X)/Pi - udint_to_real(real_to_udint(abs(X)/Pi)))*Pi ;
b:=(Pi-a) * (bool_to_real(a>1.5707964))+a* bool_to_real(not(a>=1.5707964)) ;
SIN_RAD:=((pow(b,9)/362880 + (((b-(pow(b,3)/6)) +pow(b,5)/120)- pow(b,7)/5040))-pow(b,11)/39916800)*Zn ;
end_function
Пробовал вычислить sin разными способами:
просто ряд Тейлора (Макларена) - с ограничением количества слагаемых количеством от 11 до 15:
Код:
// вычисление синуса через ряд Маклорена (Тейлора)
FUNCTION sin_1_: REAL;
VAR_INPUT
x: REAL;
END_VAR
VAR
n: UDINT;
Pi: REAL := 3.1415926535897932384626433832795;
Pi_2: REAL := 6.2831853071795864769252867665590;
a, s, f: REAL;
sign: BOOL;
END_VAR
// приведение аргумента к диапазону 0...2*Pi
IF x < 0 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_2) + 1;
x := x + UDINT_TO_REAL(n) * Pi_2;
END_IF;
IF (x > Pi_2) THEN
n := REAL_TO_UDINT(ABS(x) / Pi_2);
x := x - UDINT_TO_REAL(n) * Pi_2;
END_IF
f := 1;
a := x;
s := x;
x := x * x;
REPEAT
a := - a * x / ((f + 1) * (f + 2));
f := f + 2;
s := s + a;
IF f > 30 THEN
EXIT;
END_IF;
UNTIL (ABS(a) < 0.0000001) AND (f > 24)
END_REPEAT
sin_1_ := s;
END_FUNCTION
Пересчёт аргумента (неоднократное снижение в 2 раза) до приведения к диапазону (-1,5...+1,5), вычисление sin и cos при помощи ряда Тейлора, пересчёт результатов к исходному значению аргумента
Формула удвоения аргумента
sin(2x) = 2 sin(x) cos(x)
cos(2x) = 1 - [sin(x)]^2
пересчёт аргумента к меньшему числу, вычисление рядом Тейлора (Маклорена), пересчёт результата:
Код:
// вычисление синуса через пересчёт аргумента к узкому диапазону, в котором доступна
// высокая точность вычислений, и последующим пересчётом результата для значения,
// соответствующего исходному аргументу
FUNCTION sin_2_: REAL;
VAR_INPUT
x: REAL;
END_VAR
VAR
Pi: REAL := 3.1415926535897932384626433832795;
Pi_x2: REAL := 6.2831853071795864769252867665590;
// Pi_2:real := 1.5707963267948966192313216916398;
// Pi_4:real := 0.78539816339744830961566084581988;
// переменные для вычислений по формуле двойного аргумента
n: UDINT;
sin_2x, cos_2x: REAL;
sin_x, cos_x: REAL;
// переменные для вычислений ряда Тейлора
f: REAL;
a_sin, a_cos: REAL;
END_VAR
// показательная функция [x^(2n+1)] в первых членах суммы ряда Тейлора
// растёт быстрее факториала [(2n+1)!], что приводит к потере точности
// буквально с первых слагаемых
// Решения:
// 1. приведение аргумента к диапазону 0...2*Pi - исходной области определения sin
IF x < 0 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2) + 1;
x := x + UDINT_TO_REAL(n) * Pi_x2;
END_IF;
IF x > Pi_x2 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2);
x := x - UDINT_TO_REAL(n) * Pi_x2;
END_IF
// приводим аргумент к приемлемым значениям для вычисления
n := 0;
WHILE (ABS(x) > 1.5) DO
x := x / 2.0;
n := n + 1;
END_WHILE
// вычисляем первое приближение через ряд Тейлора
f := 1;
a_sin := x;
sin_x := x;
a_cos := 1;
cos_x := 1;
x := x * x;
REPEAT
a_sin := - a_sin * x / ((f + 1) * (f + 2));
a_cos := - a_cos * x / (f * (f + 1));
sin_x := sin_x + a_sin;
cos_x := cos_x + a_cos;
f := f + 2;
IF f > 30 THEN
EXIT;
END_IF;
UNTIL (ABS(a_sin) < 0.0000001) AND (f > 24)
END_REPEAT
// вычисляем для исходного аргумента
WHILE (n > 0) DO
sin_2x := 2 * sin_x * cos_x;
cos_2x := 1 - 2 * sin_x * sin_x;
sin_x := sin_2x;
cos_x := cos_2x;
n := n - 1;
END_WHILE
sin_2_ := sin_x;
END_FUNCTION
По аналогии с предыдущим способом, но по готовой формуле
sin(5x) = 16 [sin(x)]^5 - 20 [sin(x)]^3 + 5 sin(x)
деление аргумента на 5, вычисление sin при помощи ряда Тейлора (Маклорена), пересчёт результата по формуле:
Код:
// вычисление синуса через пересчёт аргумента к узкому диапазону, в котором доступна
// высокая точность вычислений, и последующим пересчётом результата для значения,
// соответствующего исходному аргументу
// sin(2x) = 2 sin(x) cos(x)
// sin(3x) = 3 sin(x) - 4 [sin(x)]^3
// sin(4x) = cos(x) (4 sin(x) - 8 [sin(x)]^3)
// sin(5x) = 16 [sin(x)]^5 - 20 [sin(x)]^3 + 5 sin(x)
// sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x)
FUNCTION sin_3_: REAL;
VAR_INPUT
x: REAL;
END_VAR
VAR
Pi: REAL := 3.1415926535897932384626433832795;
Pi_x2: REAL := 6.2831853071795864769252867665590;
// Pi_2:real := 1.5707963267948966192313216916398;
// Pi_4:real := 0.78539816339744830961566084581988;
// переменные для вычислений по формуле двойного аргумента
n: UDINT;
sin_2x, cos_2x: REAL;
sin_x, cos_x: REAL;
// переменные для вычислений ряда Тейлора
f: REAL;
a_sin, a_cos: REAL;
END_VAR
// показательная функция [x^(2n+1)] в первых членах суммы ряда Тейлора
// растёт быстрее факториала [(2n+1)!], что приводит к потере точности
// буквально с первых слагаемых
// Решения:
// 1. приведение аргумента к диапазону 0...2*Pi - исходной области определения sin
IF x < 0 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2) + 1;
x := x + UDINT_TO_REAL(n) * Pi_x2;
END_IF;
IF x > Pi_x2 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2);
x := x - UDINT_TO_REAL(n) * Pi_x2;
END_IF
// приводим аргумент к приемлемым значениям для вычисления
// делим на 5
x := x / 5.0;
// вычисляем первое приближение через ряд Тейлора
f := 1;
a_sin := x;
sin_x := x;
a_cos := 1;
cos_x := 1;
x := x * x;
REPEAT
a_sin := - a_sin * x / ((f + 1) * (f + 2));
a_cos := - a_cos * x / (f * (f + 1));
sin_x := sin_x + a_sin;
cos_x := cos_x + a_cos;
f := f + 2;
IF f > 30 THEN
EXIT;
END_IF;
UNTIL (ABS(a_sin) < 0.0000001) AND (f > 24)
END_REPEAT
// вычисляем для исходного аргумента
sin_2x := sin_x * sin_x;
sin_x := sin_x * ((16*sin_2x - 20)*sin_2x +5);
sin_3_ := sin_x;
END_FUNCTION
Аналогично, но аргумент делится на 7
sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x)
деление аргумента на 7, вычисление sin при помощи ряда Тейлора (Маклорена), пересчёт результата по формуле:
Код:
// вычисление синуса через пересчёт аргумента к узкому диапазону, в котором доступна
// высокая точность вычислений, и последующим пересчётом результата для значения,
// соответствующего исходному аргументу
// sin(2x) = 2 sin(x) cos(x)
// sin(3x) = 3 sin(x) - 4 [sin(x)]^3
// sin(4x) = cos(x) (4 sin(x) - 8 [sin(x)]^3)
// sin(5x) = 16 [sin(x)]^5 - 20 [sin(x)]^3 + 5 sin(x)
// sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x)
FUNCTION sin_4_: REAL;
VAR_INPUT
x: REAL;
END_VAR
VAR
Pi: REAL := 3.1415926535897932384626433832795;
Pi_x2: REAL := 6.2831853071795864769252867665590;
// Pi_2:real := 1.5707963267948966192313216916398;
// Pi_4:real := 0.78539816339744830961566084581988;
// переменные для вычислений по формуле двойного аргумента
n: UDINT;
sin_2x, cos_2x: REAL;
sin_x, cos_x: REAL;
// переменные для вычислений ряда Тейлора
f: REAL;
a_sin, a_cos: REAL;
END_VAR
// показательная функция [x^(2n+1)] в первых членах суммы ряда Тейлора
// растёт быстрее факториала [(2n+1)!], что приводит к потере точности
// буквально с первых слагаемых
// Решения:
// 1. приведение аргумента к диапазону 0...2*Pi - исходной области определения sin
// 2. приведение аргумента к эквивалентному диапазону 0...+Pi,
// при этом меняется знак аргумента, который был больше Pi
// sin(t) = sin(-x+Pi) = sin(-x)cos(Pi) + sin(Pi)cos(-x) = -sin(-x) = sin(x)
// x = Pi - t
// 3. приведение аргумента к диапазону -Pi/2...+Pi/2
// sin(t) = sin(x+Pi/2)= sin(x)cos(Pi/2) + sin(Pi/2)cos(x) = -cos(x)
IF x < 0 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2) + 1;
x := x + UDINT_TO_REAL(n) * Pi_x2;
END_IF;
IF x > Pi_x2 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2);
x := x - UDINT_TO_REAL(n) * Pi_x2;
END_IF
// приводим аргумент к приемлемым значениям для вычисления
// делим на 7
x := x / 7.0;
// вычисляем первое приближение через ряд Тейлора
f := 1;
a_sin := x;
sin_x := x;
a_cos := 1;
cos_x := 1;
x := x * x;
REPEAT
a_sin := - a_sin * x / ((f + 1) * (f + 2));
a_cos := - a_cos * x / (f * (f + 1));
sin_x := sin_x + a_sin;
cos_x := cos_x + a_cos;
f := f + 2;
IF f > 30 THEN
EXIT;
END_IF;
UNTIL (ABS(a_sin) < 0.0000001) AND (f > 24)
END_REPEAT
// вычисляем для исходного аргумента
sin_2x := sin_x * sin_x;
// sin_x := sin_x * ((16*sin_2x - 20)*sin_2x +5);
// sin(7x) = 7sin(x) - 56sin^3(x) + 112sin^5(x) - 64sin^7(x)
sin_x := sin_x * (7 + sin_2x * ( - 56.0 + sin_2x * (112.0 - 64.0 * sin_2x)));
sin_4_ := sin_x;
END_FUNCTION
Вычисление синуса через ряд Маклорена (Тейлора), дополненное суммированием при помощи алгоритма Кэхэна (Kahan)
вычисление при помощи ряда Тейлора (Маклорена), суммирование ряда по алгоритму Кэхэна:
Код:
// вычисление синуса через ряд Маклорена (Тейлора)
// дополненное суммированием при помощи алгоритма Кэхэна (Kahan)
FUNCTION sin_5_: REAL;
VAR_INPUT
x: REAL;
END_VAR
VAR
n: UDINT;
Pi: REAL := 3.1415926535897932384626433832795;
Pi_x2: REAL := 6.2831853071795864769252867665590;
a, s, f: REAL; // числа для суммирования ряда Тейлора
c, y, t: REAL; // числа для суммирования алгоритмом Кэхэна
END_VAR
// приведение аргумента к диапазону 0...2*Pi
IF x < 0 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2) + 1;
x := x + UDINT_TO_REAL(n) * Pi_x2;
END_IF;
IF x > Pi_x2 THEN
n := REAL_TO_UDINT(ABS(x) / Pi_x2);
x := x - UDINT_TO_REAL(n) * Pi_x2;
END_IF
IF x > Pi THEN
x := x - 0; // Pi_x2;
END_IF
// sin(x) = sin(Pi-x)
IF ABS(x) > ABS(Pi - x) THEN
x := Pi - x;
END_IF
// инициализация для алгоритма Кэхэна
c := 0;
// инициализация для ряда Тейлора
f := 1;
a := x;
s := x;
x := x * x;
REPEAT
a := - a * x / ((f + 1) * (f + 2)); // очередное слагаемое ряда
y := a - c; // сложение его с суммой по алгоритму Кэхэна
t := s + y;
c := (t - s) - y;
s := (s + a); // t; // сумму (s + a) заменяем на результат сложения по алгоритму
f := f + 2;
IF f > 50 THEN
EXIT;
END_IF;
UNTIL (ABS(a) < 0.00000001) AND (f > 24)
END_REPEAT
sin_5_ := s;
END_FUNCTION
Ещё пробовал выполнить приведение исходного аргумента к (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 с последующим пересчётом результата - за счёт простоты и скорости выполнения
Взял у kondor3000 по ссылке https://owen.ru/forum/showthread.php...l=1#post397789
Для себя сделал выводы что из перебранных алгоритмов
- в целом результаты сопоставимы
- предпочту использовать или просто ряд Тейлора или с несколькими делениями аргумента на 2 с последующим пересчётом результата - за счёт простоты и скорости выполнения
Сделал проще, ввёл 2 точки, где не работало 1 и -1, зато вычисление намного быстрее, точнее и цикл маленький
Скрытый текст:
Код:
function SIN_Rad: real ; //имя функции и тип данных выхода
var_input
X: real ; //входная переменная
end_var
var //объявление локальных переменных
Zn : real ;
Pi : real:=3.1415927 ;
a,b: real ;
end_var
Zn:=pow(-1,bool_to_real(EXTRACT(real_to_udint((abs(X))/Pi),0) XOR not (X>0)));
a:=(abs(X)/Pi - udint_to_real(real_to_udint(abs(X)/Pi)))*Pi ;
b:=(Pi-a) * (bool_to_real(a>1.5707964))+a* bool_to_real(not(a>=1.5707964)) ; // 1,5707964
if x=1.5707964 or x=7.853982 then
SIN_Rad:=1.0;
elsif x=-1.5707964 or x=4.712389 then
SIN_Rad:=-1.0;
else
SIN_Rad:=((pow(b,9)/362880 + (((b-(pow(b,3)/6)) +pow(b,5)/120)- pow(b,7)/5040))-pow(b,11)/39916800)*Zn ;
end_if
end_function
Последний раз редактировалось kondor3000; 07.02.2026 в 18:01.
Мне показалось, что этот алгоритм даёт не совсем точный результат для x=-0.227+2*Pi, т.е. не полного оборота. Именно поэтому и решил сказать о сопоставимости точности.
Результаты эксперимента для четырёх точек
Справедливости ради, стоит отметить, что из-за ограничений разрядной сетки и погрешности при сложении с константой 2*Pi результат для полученного аргумента
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 знаков после запятой и скорости могут быть сопоставимыми - и делаю вывод о приблизительной равноценности реализации функции.
Последний раз редактировалось FPavel; 07.02.2026 в 21:49.
Видел код Син. с использованием нескольких табличных значений арктангннса. Правда не на ST. Может адаптировать (портировать) его?
Честно, не знаю, насколько будет быстрее работать?
Последний раз редактировалось melky; 07.02.2026 в 21:39.
Мне просто не нравится код с неподходящим алгоритмом, особенно опубликованный в примерах - самый яркий это 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
Т.е. простое действие обёрнуто в функцию, содержащую вместо нескольких арифметических действий:
- перепаковку входных данных в массив
- цикл, который из-за малого числа итераций можно развернуть
- вызов затратной функции pow(a, b)=exp(b*ln(a)) вместо одного умножения
Сделал проще, ввёл 2 точки, где не работало 1 и -1, зато вычисление намного быстрее, точнее и цикл маленький
Скрытый текст:
Код:
function SIN_Rad: real ; //имя функции и тип данных выхода
var_input
X: real ; //входная переменная
end_var
var //объявление локальных переменных
Zn : real ;
Pi : real:=3.1415927 ;
a,b: real ;
end_var
Zn:=pow(-1,bool_to_real(EXTRACT(real_to_udint((abs(X))/Pi),0) XOR not (X>0)));
a:=(abs(X)/Pi - udint_to_real(real_to_udint(abs(X)/Pi)))*Pi ;
b:=(Pi-a) * (bool_to_real(a>1.5707964))+a* bool_to_real(not(a>=1.5707964)) ; // 1,5707964
if x=1.5707964 or x=7.853982 then
SIN_Rad:=1.0;
elsif x=-1.5707964 or x=4.712389 then
SIN_Rad:=-1.0;
else
SIN_Rad:=((pow(b,9)/362880 + (((b-(pow(b,3)/6)) +pow(b,5)/120)- pow(b,7)/5040))-pow(b,11)/39916800)*Zn ;
end_if
end_function
Кстати, при последовательном вычислении членов ряда ошибка не возникает. Значение функций SIN_Rad и SIN_Rad2 практически совпадают. По данным fSUB разность 5.96E-8 отмечается лишь на некоторых углах (+/- 50, 245, 255 … - проверял через 5 градусов)). Извиняюсь за грубое редактирование (только для примера).
Скрытый текст:
Код:
function SIN_Rad2: real ; //имя функции и тип данных выхода
var_input
X: real ; //входная переменная
end_var
var //объявление локальных переменных
Zn : real ;
Pi : real:=3.1415927 ;
a,b: real ;
i : udint; (* переменная цикла *)
end_var
Zn:=pow(-1,bool_to_real(EXTRACT(real_to_udint((abs(X))/Pi),0) XOR not (X>0)));
a:=(abs(X)/Pi - udint_to_real(real_to_udint(abs(X)/Pi)))*Pi ;
b:=(Pi-a) * (bool_to_real(a>1.5707964))+a* bool_to_real(not(a>=1.5707964)) ; // 1,5707964
(*
if x=1.5707964 or x=7.853982 then
SIN_Rad2:=1.0;
elsif x=-1.5707964 or x=4.712389 then
SIN_Rad2:=-1.0;
else
SIN_Rad2:=((pow(b,9)/362880 + (((b-(pow(b,3)/6)) +pow(b,5)/120)- pow(b,7)/5040))-pow(b,11)/39916800)*Zn ;
end_if
*)
// Вычисление степенного ряда Тейлора:
a:=b; (* 1-й член разложения *)
SIN_Rad2:= b; (* = 1-му члену разложения *)
b:=b*b; (* далее квадрат b *)
for i := 3 to 11 by 2 do
a:=a*b / udint_to_real(i * (i-1));(* очередной член разложения *)
SIN_Rad2:=a-SIN_Rad2;
end_for
SIN_Rad2:=-SIN_Rad2*Zn;
end_function