Розныя спосабы (і самы хуткі) для вылічэнні сінусам (косінуса) і ў Arduino

Я выкарыстоўваю плату Arduino Uno вылічыць куты маёй сістэмы (рабатызаваных рука). Куты на самай справе 10 бітаў значэння (ад 0 да 1023) ад АЛП, выкарыстоўваючы ўвесь дыяпазон АЛП. Я толькі будзе працаваць у 1-ым квадранце (ад 0 да 90 градусаў), дзе абодва сінусам і косінус з'яўляюцца станоўчымі, так што няма ніякіх праблем з адмоўнымі лікамі. Мае сумневы могуць быць выяўленыя ў 3 пытанні:

  1. Якія розныя спосабы вылічыць гэтыя трыганаметрычныя функцыі на Arduino?

  2. Які самы хуткі спосаб зрабіць тое ж самае? </Р>

  3. Ёсць грэх() і соз() функцыі ў Arduino IDE, але як жа на самай справе Arduino іх вылічэнні (як яны выкарыстоўваюць, праглядальнаму табліц або набліжэння і г.д.)? Яны, здаецца, як відавочнае рашэнне, але я хацеў бы ведаць іх фактычнай рэалізацыі, перш чым я паспрабаваць іх.

PS: Я адкрыты як для стандартнага кадавання на Arduino IDE і кадавання зборкі, а таксама любыя іншыя варыянты не згадваецца. Таксама ў мяне няма ніякіх праблем з памылкамі і набліжэнняў, якія непазбежныя для лічбавай сістэмы; Аднак, калі гэта магчыма, было б добра, каб згадаць пра ступень магчымых памылак

8
Я мяркую, што вы хацелі працаваць у градусах. Вы будзеце жадаць ўводу лікаў або дзесятковых лікаў для кута?
дададзена аўтар Yoni Baciu, крыніца
Вы б быць у парадку з набліжанымі значэннямі?
дададзена аўтар Yoni Baciu, крыніца
Не маглі б вы колькасна ацаніць вашыя патрабаванні да дакладнасці? Набліжэнне сов (π/2x) ≈ 1-х ² мае максімальную хібнасць 5.6e-2. І (1-х ²) (1-0.224x²), які каштуе 3 множання, добра ў межах 9.20e-4.
дададзена аўтар Sprogz, крыніца
Для толькі 90 (цэлы лік) градусаў даведачную табліцу 90-запіс будзе самым хуткім і эфектыўным. На самай справе для поўных 360 градусаў вы можаце выкарыстоўваць табліцу пошуку 90-уваходу. Проста чытаць яго назад для 90-179 і інвертаваць яго 180-269. У абодвух за 270-359.
дададзена аўтар Majenko, крыніца
@EdgarBonet Выбачайце за позні адказ. Я не маю ніякага току аператара фіксаванай дакладнасці. Я проста хачу ведаць усе магчымыя варыянты ў цяперашні час
дададзена аўтар Ken Arnold, крыніца
Градусы да. Я думаю, было б лягчэй пісаць код і тэст, калі мы выкарыстоўваем цэлыя лікі, так што я б з гэтым. Я буду надаваць больш выразнай інфармацыі пра рэдагаванне
дададзена аўтар Ken Arnold, крыніца
Ды на самай справе, але я хацеў бы ведаць ступень хібнасці розных метадаў. Гэта не дакладны прадукт, а пабочны праект шахты. На самай справе набліжэння непазбежныя практычна для любога (калі не маецца) лічбавая сістэма рэалізуе матэматычную функцыю
дададзена аўтар Ken Arnold, крыніца

8 адказы

Дзве асноўныя метады матэматычнага вылічэнні (з полиномами) і даведачныя табліцы.

матэматычная бібліятэка На платформе Arduino (у libm, частка Avr-LIBC) выкарыстоўвае ранейшы. Ён аптымізаваны для AVR у тым, што яна напісана з 100% асэмблеры, і як такой практычна немагчыма прытрымлівацца таго, што ён робіць (ёсць нуль каментароў, а). Будзьце ўпэўненыя, хоць гэта будзе найбольш аптымізаваныя чыста паплавок мазгі рэалізацыі далёка пераўзыходзяць нашы маглі прыдумаць.

Аднак ключ ёсць паплавок . Усё, што на Arduino з удзелам плавае кропкай будзе цяжкавагавіком ў параўнанні з чыстым цэлы лік, а так як вы толькі запытваючаму цэлыя лікі ад 0 да 90 градусаў просты пошук у табліцы на сённяшні дзень з'яўляецца найбольш простым і эфектыўным спосабам.

Табліца 91 значэнняў дасць вам усё, ад 0 да 90 ўключна. Тым не менш, калі вы зробіце што табліца значэнняў з якая плавае кропкай паміж 0.0 і 1.0 вы па-ранейшаму затым неэфектыўнасцю працы з паплаўкамі (прадастаўлена не як неэфектыўнымі, так як вылічэнні грэх з паплаўкамі), таму захаванне значэння з фіксаванай кропкай замест гэтага будзе значна больш эфектыўным.

Гэта можа быць як просты, як Які захоўвае значэнне, памножанае на 1000, таму вы маеце ад 0 да 1000, а не ад 0,0 да 1,0 (напрыклад граху (30) будзе захоўвацца ў выглядзе 500 замест 0,5). Больш эфектыўным будзе захоўваць значэння, як, напрыклад, значэнне В16, дзе кожнае значэнне (біт) уяўляе сабой 1/65536th 1,0. Гэтыя значэння Q16 (і звязаныя з Q15, Q1.15 і г.д.) з'яўляюцца больш эфектыўнымі для працы з паколькі ў вас ёсць паўнамоцтвы двое дзяцей, якія любяць кампутары працаваць з замест паўнамоцтваў-на-дзесяці, якія яны ненавідзяць, якія працуюць з.

Не забывайце таксама, што sin() функцыя чакае радыян, таму спачатку неабходна пераўтварыць цэлыя ступені ў значэнне з якая плавае коскі ў радыянах, што робіць выкарыстанне sin() яшчэ больш неэфектыўным па параўнанні з даведачнай табліцы, якая можа працаваць непасрэдна з цэлалікавых значэннем градусаў.

Спалучэнне гэтых двух сцэнарыяў, хоць, магчыма. Лінейная інтэрпаляцыя дазволіць вам атрымаць набліжэнне кута з якая плавае коскі паміж двума цэлымі лікамі. Гэта так проста, як працуе, як далёка паміж двума кропкамі ў табліцы пошуку вы і стварэнне сярэдняўзважаны на аснове гэтай адлегласці двух значэнняў. Напрыклад, калі вы знаходзіцеся на 23,6 градусаў вы прымаеце (sintable [23] * (1-0.6)) + (sintable [24] * 0.6) . У асноўным ваша сінусоіда ператвараецца ў чараду дыскрэтных кропак, злучаных паміж сабой прамымі лініямі. Вы гандлюеце дакладнасці для хуткасці.

8
дададзена
Я напісаў бібліятэку некаторы час таму, што выкарыстоўваецца Паліны Тэйлара для SIN/COS, які быў хутчэй, чым бібліятэка. Улічваючы, што я выкарыстаў радыян з якая плавае коскі ў якасці ўваходных дадзеных для абодвух.
дададзена аўтар tuskiomi, крыніца

Ёсць некаторыя добрыя адказы тут, але я хацеў бы дадаць метад, які не быў згаданы яшчэ адзін, вельмі добра падыходзіць для вылічэнні трыганаметрычных функцый на ўбудавальных сістэмах, і гэта CORDIC тэхніка Wiki Уваход тут Можна вылічыць трыганаметрычныя функцыі, выкарыстоўваючы толькі зрухі і дадае і невялікая даведачную табліцу.

Вось грубы прыклад у C. У рэчаіснасці, ён рэалізуе функцыю atan2 Бібліятэкі C() з выкарыстаннем CORDIC (г.зн. знайсці кут зададзеных два артаганальных кампанент.) Ён выкарыстоўвае з якая плавае кропкай, але можа быць адаптаваны для выкарыстання з фіксаванай кропкай арыфметыкі.

/*
 * Simple example of using the CORDIC algorithm.
 */

#include 
#include 

#define CORDIC_TABLE_SIZE  16

double cordic_table[CORDIC_TABLE_SIZE];

void init_table(void);
double angle(double I, double Q);

/*
 * Given a sine and cosine component of an
 * angle, compute the angle using the CORIDC
 * algoritm.
 */
double angle(double I, double Q)
{
    int L;
    double K = 1;
    double angle_acc = 0;
    double tmp_I;

    if (I < 0) {
        /* rotate by an initial +/- 90 degrees */
        tmp_I = I;
        if (Q > 0.0) {
            I = Q;           /* subtract 90 degrees */
            Q = -tmp_I;
            angle_acc = -90;
        } else {
            I = -Q;          /* add 90 degrees */
            Q = tmp_I;
            angle_acc = 90;
        }
    } else {
        angle_acc = 0;
    }

    /* rotate using "1 + jK" factors */
    for (L = 0, K = 1; L <= CORDIC_TABLE_SIZE; L++) {
        tmp_I = I;
        if (Q >= 0.0) {
            /* angle is positive: do negative roation */
            I += Q * K;
            Q -= tmp_I * K;
            angle_acc -= cordic_table[L];
        } else {
            /* angle is negative: do positive rotation */
            I -= Q * K;
            Q += tmp_I * K;
            angle_acc += cordic_table[L];
        }
        K /= 2.0;
    }
    return -angle_acc;
}

void init_table(void)
{
    int i;
    double K = 1;

    for (i = 0; i < CORDIC_TABLE_SIZE; i++) {
        cordic_table[i] = 180 * atan(K)/M_PI;
        K /= 2.0;
    }
}
int main(int argc, char **argv)
{
    double I, Q, A, Ar, R, Ac;

    init_table();

    printf("# Angle,    CORDIC Angle,  Error\n");
    for (A = 0; A < 90.0; A += 0.5) {

        Ar = A * M_PI/180; /* convert to radians for C's sin & cos fn's */

        R = 5; //Arbitrary radius

        I = R * cos(Ar);
        Q = R * sin(Ar);

        Ac = angle(I, Q);
        printf("%9f, %9f,   %12.4e\n", A, Ac, Ac-A);
    }
    return 0;
}

Але паспрабуйце роднай Arduino функцыі першага акуратны - яны могуць быць досыць хутка, так ці інакш.

5
дададзена
Я прыняў падобны падыход у мінулым, на STM8. яна прымае два крокі: 1) вылічыць sin (х) і соз (х) ад граху (2x), а затым 2) вылічыць sin (х +/- х/2) ад граху (х), SIN (х/2) , соз (х) і соз (х/2) -> праз ітэрацыю вы можаце падысці да вашай мэты. у маім выпадку, я пачаў з 45 градусаў (0,707), і працаваў мой шлях да мэты. гэта значна больш павольна, чым функцыя стандартнага антиреверс Sin ().
дададзена аўтар dannyf, крыніца

Я гуляў трохі з вылічальнымі сінусам і косінус на Arduino з выкарыстаннем фіксаванай кропкай паліномны набліжэнняў. вось мае вымярэння сярэдняга часу выканання і горшай памылкі выпадку, у параўнанні са стандартам соз() і sin() з-Avr LIBC:

function    max error   cycles   time
-----------------------------------------
cos_fix()   9.53e-5     108.25    6.77 µs
sin_fix()   9.53e-5     110.25    6.89 µs
cos()       2.98e-8     1720.8   107.5 µs
sin()       2.98e-8     1725.1   107.8 µs

It's based on a 6th degree polynomial computed with only 4 multiplications. The multiplications themselves are done in assembly, as I found that gcc implemented them inefficiently. The angles are expressed as uint16_t in units of 1/65536 of a revolution, which makes the arithmetic of angles naturally work modulo one revolution.

Калі вы думаеце, што можа задаволіць свой кошт, вось код: з фіксаванай кропкай трыганаметрыі . На жаль, я да гэтага часу не перавялі гэтую старонку, якая па-французску, але вы можна зразумець ўраўненні і код (імёны зменных, каментары ...) на англійскай мове.


Edit: Since the server seems to have vanished, here is some info on the approximations I found.

Я хацеў напісаць куты ў двайковай з фіксаванай кропкай, у адзінках квадранце (Або, што тое ж самае, па чарзе). І я таксама хацеў бы выкарыстаць яшчэ Паліна, так як яны з'яўляюцца больш эфектыўнымі, чым для вылічэнні адвольнага мнагачлена. Іншымі словамі, я хацеў Паліна P() такое, што

сов (π/2 х) ≈ Р (х 2 ) пры х ∈ [0,1]

I also required the approximation to be exact at both ends of the interval, to ensure that cos(0) = 1 and cos(π/2) = 0. These constraints led to the form

Р (і) = (1 - і) (1 + Іа (і))

дзе Q() уяўляе сабой адвольны мнагачлена.

Далей, я шукаў лепшае рашэнне ў залежнасці ад ступені Q() і знайшоў гэта:

        Q(u)             │ degree of P(x²) │ max error
─────────────────────────┼─────────────────┼──────────
          0              │         2       │  5.60e-2
       −0.224            │         4       │  9.20e-4
−0.2335216 + 0.0190963 u │         6       │  9.20e-6

Выбар сярод вышэйзгаданых рашэнняў з'яўляецца хуткасць/дакладнасць кампраміс. Трэцяе рашэнне дае больш дакладнасці, чым дасягальна з 16 бітамі, і гэта адзін я абраў для рэалізацыі ў 16-бітным.

5
дададзена
@TLW: дадаў, што да адказу.
дададзена аўтар Sprogz, крыніца
@TLW: я патрабаваў таго, каб мець некаторыя "добра" ўласцівасці (напрыклад, COS (0) = 1), якія абмежаваныя ў форме (1-х ²) (1 + x²Q (x²)), дзе Q (і) адвольны Паліна (гэта растлумачана на старонцы). Я ўзяў першай ступені Q (толькі 2 каэфіцыенты), знойдзеныя набліжаныя каэфіцыенты пры прыступе, а затым ўручную наладзіў аптымізацыі метадам спроб і памылак.
дададзена аўтар Sprogz, крыніца
Гэта дзіўна, @Edgar.
дададзена аўтар SDsolar, крыніца
Што вы зрабілі, каб знайсці паліном?
дададзена аўтар ThomasX, крыніца
@EdgarBonet - цікава. Звярніце ўвагу, што гэтая старонка не загружаецца для мяне, хоць кэшаваных работ. Не маглі б вы дадаць Паліна, які выкарыстоўваецца для гэтага адказу?
дададзена аўтар ThomasX, крыніца

Табліца пошуку будзе самым хуткім спосабам знайсці сінусы. І калі вы зручнае вылічэнне з лікамі з фіксаванай кропкай (цэлыя лікі, двайковай кропка знаходзіцца дзесьці іншае, чым справа біт-0), вашыя далейшыя разлікі з сінуса будуць нашмат хутчэй. Гэтая табліца можа затым быць табліца слоў, магчыма, у Flash, каб захаваць прастору АЗП. Звярніце ўвагу, што ў вашай матэматыцы вам, магчыма, давядзецца выкарыстоўваць доўгія пазіцыі для вялікіх прамежкавых вынікаў.

2
дададзена

Вы можаце стварыць некалькі функцый, якія выкарыстоўвае лінейную апраксімацыю для вызначэння sin() і соз() пэўнага кута.

Я маю на ўвазе нешта накшталт гэтага:

Для кожнага я зламала графічнае прадстаўленне граху() і соз() на 3 секцыі і зрабілі лінейнае набліжэнне гэтага падзелу.

Ваша функцыя, у ідэале спачатку праверыць, што дыяпазон анёла ад 0 да 90.
Тады ён будзе выкарыстоўваць IfElse заяву, каб вызначыць, якія з 3-х секцый яна належыць, а затым робіць адпаведны лінейны разлік (г.зн. выхад = ТХ + з )

2
дададзена
Не абавязкова. Вы маглі б гэта так выхад маштабуецца паміж 0-100 замест 0-1. Такім чынам, вы маеце справу з цэлымі лікамі, а не з якая плавае кропкай. Заўвага: 100 было адвольным. Там няма ніякіх падставаў, што вы не можаце маштабаваць выхад паміж 0-128 або 0-512 або 0-1000 або 0-1024. Выкарыстоўваючы кратнае 2, вам трэба зрабіць зрух направа, каб маштабаваць вынік назад толькі.
дададзена аўтар Yoni Baciu, крыніца
Даволі разумны, @sa_leinad. Upvote. Я памятаю, як гэта рабіць пры працы з зрушэння транзістараў.
дададзена аўтар SDsolar, крыніца
Ці не будзе гэта ўключаць з якая плавае кропкай множання?
дададзена аўтар Ken Arnold, крыніца

Я паглядзеў на іншых людзей, якія апраксімуецца Cos() і грэх (), і я наткнуўся на гэты адказ:

адказ DTB на «Fast Sin/Cos, выкарыстоўваючы загадзя вылічаны масіў перакладу»

У асноўным ён падлічыў, што функцыя Math.sin() з матэматычнай бібліятэкі была хутчэй, чым пры выкарыстанні даведачнай табліцы значэнняў. Але ад таго, што я магу сказаць, гэта было разлічана на ПК.

Arduino мае матэматычную бібліятэку уключанага што можна вылічыць sin() і соз ().

2
дададзена
ПК маюць FPUs убудаваныя ў іх, якія робяць гэта хутка. Arduino зробім не так, і гэта робіць яго павольна.
дададзена аўтар Majenko, крыніца
Адказ таксама для C#, які робіць такія рэчы, як межы масіва праверкі.
дададзена аўтар Longdaysjourneyintocode, крыніца

generally, look-up table > approximation -> calculation. ram > flash. integer > fixed point > floating point. pre-calclation > real time calculation. mirroring (sine to cosine or cosine to sine) vs. actual calculation/look-up....

кожны з іх мае свае плюсы і мінусы.

Вы можаце зрабіць усё віды камбінацый, каб убачыць, што працуе лепш для вашага прыкладання.

Рэдагаванне: Я зрабіў хуткую праверку. выкарыстоўваючы 8-бітнае цэлае выснову, вылічэнне значэнняў 1024 грашаць даведачную табліцу прымае 0.6ms і 133ms з якія плаваюць ці 200x павальнейшай.

1
дададзена

У мяне было пытанне прапановы гэтага тыпу OP. Я хацеў зрабіць табліцу LUT для вылічэнні першага квадранце сінусоіднай функцыі як беззнаковых 16-бітных цэлых лікаў, пачынаючы ад 0x8000 да 0xFFFF. І я скончыў пісаць гэта для задавальнення і прыбытку. Заўвага: Гэта будзе працаваць больш эфектыўна, калі я выкарыстаў «калі» заявы. Акрамя таго, гэта не вельмі дакладна, але будзе дастаткова дакладным для сінусоіднай хвалі ў гукавой сінтэзатар

void sin_lut_ctor(){

//Make a Look Up Table for 511 terms of the sine function.
//Plugin in some polynomials to do some magic
//and you get an aproximation for sines up to π/2.
//

//All sines yonder π/2 can be derived with math

const uint16_t uLut_d = 0x0200; //maximum LUT depth for π/2 terms. 
uint16_t uLut_0[uLut_d];        //The LUT itself.
//Put the 2 above before your void setup() as global variables.
//This coefficients will only work for uLut_d = 511.

uint16_t arna_poly_0 = 0x000a;//11
uint16_t arna_poly_1 = 0x0001;//1
uint16_t arna_poly_2 = 0x0007;//7
uint16_t arna_poly_3 = 0x0001;//1   Precalculated Polynomials
uint16_t arna_poly_4 = 0x0001;//1   
uint16_t arna_poly_5 = 0x0007;//7
uint16_t arna_poly_6 = 0x0002;//2
uint16_t arna_poly_7 = 0x0001;//1

uint16_t Imm_UI_0 = 0x0001;             // Itterator
uint16_t Imm_UI_1 = 0x007c;             // An incrementor that decreases in time

uint16_t Imm_UI_2 = 0x0000;             // 
uint16_t Imm_UI_3 = 0x0000;             //             
uint16_t Imm_UI_4 = 0x0000;              //
uint16_t Imm_UI_5 = 0x0000;              //
uint16_t Imm_UI_6 = 0x0000;             // Temporary variables
uint16_t Imm_UI_7 = 0x0000;              //
uint16_t Imm_UI_8 = 0x0000;              //
uint16_t Imm_UI_9 = 0x0000;              //
uint16_t Imm_UI_A = 0x0000;
uint16_t Imm_UI_B = 0x0000;

uint16_t Imm_UI_A = uLut_d - 0x0001;    // 510

uLut_0[0x0000] = 0x8000;        //Assume that the middle point is 32768 (0x8000 hex)
while (Imm_UI_0 < Imm_UI_A) //Construct a quarter of the sine table
  {
Imm_UI_2++;                                   //Increase temporary variable by 1

Imm_UI_B = Imm_UI_2/arna_coeff_0;           //Divide it with the first coefficient (note: integer division)
Imm_UI_3 += Imm_UI_B;                         //Increase the next temporary value if the first one has increased up to the 1st coefficient
Imm_UI_1 -= Imm_UI_B;                         //Decrease the incrementor if this is the case
Imm_UI_2 *= 0x001 - Imm_UI_B;                 //Set the first temporary variable back to 0

Imm_UI_B = Imm_UI_3/arna_poly_1;           //Do the same thing as before with the next set of temporary variables
Imm_UI_4 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_3 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_4/arna_poly_2;           //And again... and again... you get the idea.
Imm_UI_5 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_4 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_5/arna_poly_3;
Imm_UI_6 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_5 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_6/arna_poly_4;
Imm_UI_7 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_6 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_7/arna_poly_5;
Imm_UI_8 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_7 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_8/arna_poly_6;
Imm_UI_9 += Imm_UI_B;
Imm_UI_1 -= Imm_UI_B;
Imm_UI_8 *= 0x0001 - Imm_UI_B;

Imm_UI_B = Imm_UI_9/arna_poly_7          //the last set won't need to increment a next variable so skip the step where you would increase it.
Imm_UI_1 -= Imm_UI_B;
Imm_UI_9 *= 1 - Imm_UI_B;

uLut_0[Imm_UI_0] = (uLut_0[Imm_UI_0 - 0x0001] + Imm_UI_1); //Set the current value as the previous one increased by our incrementor
Imm_UI_0++;              //Increase the itterator
  }   
  uLut_0[Imm_UI_A] = 0xffff; //Lastly, set the last value to 0xffff

  //And there you have it. A sine table with only one if statement (a while loop)
}

Зараз, каб атрымаць назад значэнне, выкарыстоўвайце гэты function.It прымае значэнне ад 0x0000 да 0x0800 і вяртае адпаведнае значэнне з LUT

uint16_t lu_sin(uint16_t lu_val0)
{
  //Get a value from 0x0000 to 0x0800. Return an appropriate sin(value)
  Imm_UI_0 = lu_val0/0x0200; //determine quadrant
  Imm_UI_1 = lu_val0%0x0200; //Get which value
  if (Imm_UI_0 == 0x0000)
  {
    return uLut_0[Imm_UI_1];
  }
  if (Imm_UI_0 == 0x0001)
  {
    return uLut_0[0x01ff - Imm_UI_1];
  }
  if (Imm_UI_0 == 0x0002)
  {
    return 0xffff - uLut_0[Imm_UI_1];
  }
  if (Imm_UI_0 == 0x0003)
  {
    return 0xffff - uLut_0[0x01ff - Imm_UI_1];
  }
}// I'm using if statements here but similarly to the above code block, 
 //you can do without. just with integer divisions and modulos

Памятаеце, што гэта не самы эфектыўны падыход да вырашэння гэтай задачы, я проста не мог зразумець, як зрабіць шэраг Тэйлара выдаваць вынікі ў адпаведным дыяпазоне.

1
дададзена
Ваш код ня кампілюецца: Imm_UI_A двойчы абвешчаны, а і некаторыя аб'явы зменных адсутнічае, а uLut_0 павінен быць глабальным. Пры наяўнасці неабходных выпраўленняў, lu_sin() хутка (ад 27 да 42 цыклаў працэсара), але <�я> вельмі недакладныя (максімальная памылка ≈ 5.04e-2). Я не магу атрымаць кропку з гэтых «Arnadathian полиномов»: здаецца, даволі цяжкі вылічэнні, але вынік амаль гэтак жа дрэнна, як просты квадратычнай апраксімацыі. Метад таксама мае велізарныя выдаткі памяці. Было б лепш, каб вылічыць табліцу на кампутары, і змясціць яго ў зыходным кодзе як PROGMEM масіў.
дададзена аўтар Sprogz, крыніца