4.4 Precizia numărului

Erorile asociate cu reprezentarea numărului sunt foarte asemănătoare erorilor de cuantificare pe durata ADC. Ați dori să stocați un interval continuu de valori; cu toate acestea, puteți reprezenta doar un număr finit de nivele cuantificate. De fiecare dată când este generat un nou număr, după un calcul matematic, de exemplu, acesta trebuie rotunjit la cea mai apropiată valoare care poate fi stocată în formatul pe care îl utilizați.

De exemplu, imaginați-vă că alocați 32 de biți pentru a stoca un număr. Deoarece există exact 232 = 4.294.967.296 posibile modele de biți diferite, puteți reprezenta exact 4.294.967.296 numere diferite. Unele limbaje de programare permit o variabilă numită întreg lung, stocată ca 32 de biți, virgulă fixă, complementul lui doi. Aceasta înseamnă că cele 4.294.967.296 modele posibile de biți reprezintă întregi între -2.147.483.648 și 2.147.483.647. În comparație, virgula mobilă cu precizie simplă răspândește aceste 4.294.967.296 modele de biți pe intervalul mult mai mare: -3,4 × 1038 la 3,4 × 1038.

Cu variabilele în virgulă fixă, spațiile dintre numerele adiacente sunt întotdeauna exact unu. În notația cu virgulă mobilă, spațiile dintre numerele adiacente variază pe intervalul de numere reprezentate. Dacă alegem în mod aleator un număr cu virgulă mobilă, spațiul următor acelui număr este de aproximativ zece milioane de ori mai mic decât numărul însuși (pentru a fi exact, 2-24 to 2-23 de ori numărul). Acesta este un concept cheie de notație în virgulă mobilă: numerele mari au spații mari între ele, în timp ce numerele mici au spații mici. Figura 4-3 ilustrează acest lucru prin afișarea numerelor în virgulă mobilă consecutive și a spațiilor care le separă.

Figura 4-3

Exemple de spațiere între numere în virgulă mobilă cu simplă precizie.

Spațierea între numere adiacente este întotdeauna în jur de 1 parte la 8 milioane și 1 parte la 17 milioane din valoarea numărului.

Programul din Tabelul 4-1 ilustrează modul în care eroarea de rotunjire (eroarea de cuantificare în calcule matematice) cauzează probleme în DSP. În bucla de program, la variabila în virgulă mobilă X se adună două numere aleatorii, apoi se scad din nou. În mod ideal, acest lucru nu ar trebui să facă nimic. În realitate, eroarea de rotunjire a fiecărei operații aritmetice face ca valoarea lui X să se îndepărteze treptat de valoarea inițială. Această deviere poate lua una din cele două forme, în funcție de modul în care erorile se adună împreună. Dacă erorile de rotunjire sunt pozitive și negative aleator, valoarea variabilei va crește și scădea în mod aleatoriu. Dacă erorile sunt predominant de același semn, valoarea variabilei se va îndepărta mult mai rapid și uniform.

Tabelul 4-1 Program pentru demonstrarea acumulării erorilor la virgula mobilă.

Acest program setează inițial valoarea lui X la 1,000000 și apoi rulează printr-o buclă care, ideal, nu ar trebui să facă nimic. Pe durata fiecărei bucle, două numere aleatoare, A și B, sunt adunate la X, și apoi scăzute înapoi. Eroarea acumulată de la aceste adunări și scăderi determină X să difere de valoarea sa inițială. După cum arată fig. 4-4, eroarea poate fi aleatoare sau aditivă.

Figura 4-4 Acumularea erorii de rotunjire la variabilele în virgulă mobilă.

Aceste curbe sunt generate prin programul arătat în tabelul 4-1. Când o variabilă în virgulă mobilă este utilizată repetat în operații matematice, eroarea de rotunjire acumulată cauzează deviația valorii variabilei. Dacă erorile sunt și pozitive și negative, valoarea va crește sau va descrește în mod aleator. Dacă erorile de rotunjire sunt predominant de același semn, valoarea se va schimba într-o manieră mult mai rapidă și uniformă.

Figura 4-4 arată modul cum variabila X, în acest exemplu de program, deviază în valoare. O preocupare evidentă este că eroarea aditivă este mult mai rea decât eroarea aleatoare. Acest lucru se datorează faptului că erorile aleatorii tind să se anuleze unal cu alta, în timp ce erorile aditive se acumulează pur și simplu. Eroarea aditivă este aproximativ egală cu eroarea de rotunjire dintr-o singură operație, înmulțită cu numărul total de operații. În comparație, eroarea aleatoare crește doar proporțional cu rădăcina pătrată a numărului de operații. Așa cum se arată în acest exemplu, eroarea aditivă poate fi de sute de ori mai rea decât eroarea aleatoare pentru algoritmii DSP obișnuiți.

Din păcate, este aproape imposibil să se controleze sau să se prevadă care dintre aceste două comportamente va avea un algoritm particular. De exemplu, programul din Tabelul 4-1 generează o eroare aditivă. Acest lucru poate fi schimbat la o eroare aleatoare prin simpla modificare a numerelor adunate și scăzute. În particular, curba de eroare aleatoare din figura 4-4 a fost generată prin definirea: A = EXP (RND) și B = EXP (RND), mai degrabă decât: A = RND și B = RND. În loc de A și B fiind numere repartizate aleatoriu între 0 și 1, ele devin valori distribuite exponențial între 1 și 2.718. Chiar și această mică modificare este suficientă pentru a comuta modul de acumulare a erorilor.

Deoarece nu putem controla modul în care se acumulează erorile de rotunjire, țineți cont de scenariul cel mai rău. Așteptați ca fiecare număr de precizie simplă să aibă o eroare de aproximativ o parte din patruzeci de milioane, înmulțită cu numărul de operații prin care a trecut. Aceasta se bazează pe ipoteza unei erori aditive, iar eroarea medie dintr-o singură operație este un sfert din nivelul de cuantificare. Prin aceeași analiză, fiecare număr de precizie dublă are o eroare de aproximativ o parte la patruzeci de miliarde, înmulțită cu numărul de operații.

Tabelul 4-2 Comparația variabilelor în virgulă mobilă și întregi pentru controlul buclei.

Programul din partea stângă controlează bucla FOR-NEXT cu o variabilă X în virgulă mobilă. Aceasta rezultă într-o eroare de rotunjire acumulată de 0, 000133 la sfârșitul programului, cauzând valoarea ultimei bucle X=10.0 să fie omisă. In comparație, programul din partea dreaptă utilizează un întreg, I%, pentru a indexa bucla. Aceasta oferă precizia perfectă și garantează că numărul corect de cicluri ale buclei va fi completat.

Tabelul 4-2 ilustrează o problemă deosebit de enervantă de eroare de rotunjire. Fiecare dintre cele două programe din acest tabel îndeplinește aceeași sarcină: imprimarea a 1001 de numere egal spațiate între 0 și 10. Programul din stânga utilizează variabila X în virgulă mobilă, ca index de buclă. Când este instruit să execute o buclă, computerul începe prin a seta variabila index la valoarea de pornire a buclă (0 în acest exemplu). La sfârșitul fiecărui ciclu de buclă, mărimea pasului (0,01 în cazul) este adăugată la index. Se ia apoi o decizie: sunt necesare mai multe cicluri de bucle sau este realizată bucla? Buclele se termină atunci când computerul constată că valoarea indexului este mai mare decât valoarea de terminare (în acest exemplu, 10,0). Așa cum este arătat de ieșirea generată, eroarea de rotunjire a adaosurilor determină valoarea X să acumuleze o discrepanță semnificativă pe parcursul buclei. De fapt, eroarea acumulată împiedică executarea ultimului ciclul al buclei. În loc ca X să aibă valoarea 10,0 în ultimul ciclu, eroarea face ca ultima valoare a lui X să fie egală cu 10,000133. Din moment ce X este mai mare decât valoarea de terminare, calculatorul crede că lucrarea sa se termină și bucla se termină prematur. Această ultimă valoare lipsă este un bug (eroare) comun în multe programe de calculator.

În comparație, programul din dreapta folosește o variabilă întreg, I%, pentru a controla bucla. Adunarea, scăderea sau multiplicarea a două numere întregi produce întotdeauna un alt număr întreg. Aceasta înseamnă că notația în virgulă fixă nu are absolut nicio eroare de rotunjire cu aceste operații. Intregii sunt ideali pentru controlul buclelor, precum și a altor variabile care sunt supuse mai multor operații matematice. Ultimul ciclu de buclă este garantat să se execute! Cu excepția cazului în care aveți o puternică motivație de a face altfel, folosiți întotdeauna numere întregi pentru indici și contoare.

Dacă trebuie să utilizați o variabilă în virgulă mobilă ca index de buclă, încercați să utilizați fracțiuni care sunt o putere a lui doi (cum ar fi: 1/2, 1/4, 3/8, 27/16), în loc de o putere a lui zece (cum ar fi: 0,1; 0,6; 1,4; 2,3 etc.). De exemplu, ar fi mai bine să utilizați: FOR X = 1 TO 10 STEP 0.125, mai degrabă decât: FOR X = 1 TO 10 STEP 0.1. Acest lucru permite indexului să aibă întotdeauna o reprezentare binară exactă, reducând astfel eroarea de rotunjire. De exemplu, numărul zecimal: 1,125, poate fi reprezentat exact în notație binară: 1.001000000000000000000000 ×20. În comparație, numărul zecimal: 1,1 cade între două numere în virgulă mobilă: 1,0999999046 și 1,1000000238 (în binar aceste numere sunt: ​​1.00011001100110011001100 ×20 și 1.00011001100110011001101×20). Acest lucru are ca rezultat o eroare inerentă de fiecare dată când 1,1 se întâlnește într-un program.

Un fapt util de reținut: virgula mobilă în precizie simplă are o reprezentare binară exactă pentru fiecare număr întreg între ± 16,8 milioane (pentru a fi exact, ±224 ). Deasupra acestei valori, spațiile dintre nivele sunt mai mari decât unu, cauzând pierderi ale unor valori de numere întregi. Aceasta permite numerelor întregi în virgulă mobilă (între ± 16,8 milioane) să se adune, să se scadă și să se înmulțească, fără eroare de rotunjire.

Secțiunea următoare: Viteză de execuție: Limbaj de programare