Страница 63 из 64 ПерваяПервая ... 135361626364 ПоследняяПоследняя
Показано с 621 по 630 из 634

Тема: Создать функцию на ST

  1. #621

    По умолчанию

    Цитата Сообщение от zjWlad Посмотреть сообщение
    kondor3000.
    Функция SIN_Rad для углов 90, 270 градусов (после пересчета в радианы) вместо верного результата выдает ноль. Одно из «магических» значений аргумента 1,5707964. При изменении последней цифры в любую сторону – все нормально. Может это только у меня. (OWEN Logic 2.6.347.0, на OWEN Logic 1.23 тоже самое)
    Магическое значение, очевидно - пи/2

  2. #622

    По умолчанию

    Цитата Сообщение от 1exan Посмотреть сообщение
    Магическое значение, очевидно - пи/2
    Да, но не только.

  3. #623

    По умолчанию

    Цитата Сообщение от zjWlad Посмотреть сообщение
    kondor3000.
    Функция SIN_Rad для углов 90, 270 градусов (после пересчета в радианы) вместо верного результата выдает ноль. Одно из «магических» значений аргумента 1,5707964. При изменении последней цифры в любую сторону – все нормально. Может это только у меня. (OWEN Logic 2.6.347.0, на OWEN Logic 1.23 тоже самое)
    У каждой функции есть свои ограничения, здесь простота и маленький цикл программы, https://owen.ru/forum/showthread.php...l=1#post397789
    В отличие от рядов Тейлора, в нескольких функциях, с огромным циклом программы (порядка 72 мс).
    изначально некоторые функции использовались в астротаймере, где углы ограничены. https://owen.ru/forum/showthread.php...696#post448696
    Последний раз редактировалось kondor3000; 06.02.2026 в 15:06.

  4. #624

    По умолчанию

    Цитата Сообщение от 1exan Посмотреть сообщение
    Магическое значение, очевидно - пи/2
    Да, но оно не единственное.

  5. #625

    По умолчанию

    Взял у 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 с последующим пересчётом результата - за счёт простоты и скорости выполнения
    Вложения Вложения

  6. #626

    По умолчанию

    Цитата Сообщение от FPavel Посмотреть сообщение
    Взял у 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.

  7. #627

    По умолчанию

    Мне показалось, что этот алгоритм даёт не совсем точный результат для x=-0.227+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
    Справедливости ради, стоит отметить, что из-за ограничений разрядной сетки и погрешности при сложении с константой 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.

  8. #628
    Пользователь
    Регистрация
    27.11.2011
    Адрес
    Краснодар
    Сообщений
    13,158

    По умолчанию

    Видел код Син. с использованием нескольких табличных значений арктангннса. Правда не на ST. Может адаптировать (портировать) его?
    Честно, не знаю, насколько будет быстрее работать?
    Последний раз редактировалось melky; 07.02.2026 в 21:39.

  9. #629

    По умолчанию

    Можно попробовать...

    Мне просто не нравится код с неподходящим алгоритмом, особенно опубликованный в примерах - самый яркий это 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)) вместо одного умножения

    А ведь на этот код Алиса смотрит! Стыдно же!

  10. #630

    По умолчанию

    Цитата Сообщение от kondor3000 Посмотреть сообщение
    Сделал проще, ввёл 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

Страница 63 из 64 ПерваяПервая ... 135361626364 ПоследняяПоследняя

Похожие темы

  1. Ответов: 14
    Последнее сообщение: 01.07.2023, 21:30
  2. Ответов: 6
    Последнее сообщение: 22.12.2021, 10:50
  3. Ответов: 3
    Последнее сообщение: 13.09.2021, 13:31
  4. ПЛК160. Чем заменить функцию записи 0x05?
    от FallenDAY в разделе ПЛК1хх
    Ответов: 4
    Последнее сообщение: 26.08.2017, 13:19
  5. Как написать собственную функцию wait()
    от PavelKazakov в разделе ПЛК1хх
    Ответов: 3
    Последнее сообщение: 23.07.2009, 11:37

Метки этой темы

Ваши права

  • Вы не можете создавать новые темы
  • Вы не можете отвечать в темах
  • Вы не можете прикреплять вложения
  • Вы не можете редактировать свои сообщения
  •