Код:
FUNCTION sin_enh: 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;
// n:=0;
x := x + udint_to_real(n) * Pi_x2;
end_if;
if x > Pi_x2 then
n := real_to_udint(abs(x) / Pi_x2);
// n:=0;
x := x - udint_to_real(n) * Pi_x2;
end_if
*)
// для проверки сразу привожу к проблемному диапазону
x := x + Pi + Pi;
// приводим аргумент к приемлемым значениям для вычисления
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_enh := sin_x;
END_FUNCTION