Salta al contenuto
Note per Studenti Esercizio 19 · realizzazione in C di un filtro con coefficienti maggiori di 1 a virgola fissa (tema d'esame febbraio 2026)

Esercizio 19realizzazione in C di un filtro con coefficienti maggiori di 1 a virgola fissa (tema d'esame febbraio 2026)

Esame
In questa pagina 5

Testo (tema d'esame febbraio 2026, problema P3). Si deve calcolare y(k)=0,8 y(k−1)+12,2 x(k)−12 x(k−1)y(k)=0{,}8\,y(k-1)+12{,}2\,x(k)-12\,x(k-1) (il filtro dell'esercizio 17) con un processore a virgola fissa a 32 bit. Si chiede di: (a) rappresentare i coefficienti a=0,8a=0{,}8, b0=12,2b_0=12{,}2, b1=−12b_1=-12 su 16 bit nella notazione che minimizza l'errore; (b) scrivere l'espressione in linguaggio C che realizza l'algoritmo, assumendo xx in formato frazionario 0.16 U (Q16) e yy in formato 4.12 U; (c) il valore massimo di xx che non causa overflow nel calcolo; (d) verificare il comportamento con un gradino unitario.


Teoria usata: Realizzare un filtro o un regolatore a virgola fissaRealizzare in C un filtro a virgola fissa vuol dire scegliere per ogni dato il formato (coefficienti $<1$ in 0.16 U, coefficienti $>1$ in 1.15, 4.12, 5.11 S...), moltiplicare con aritmetica a 32 bit ($16\times16\to32$) e riportare il prodotto al formato di arrivo con uno shift a destrascorrimento dei bit verso destra: equivale a dividere per una potenza di 2; per sommare due prodotti con scale diverse si fa lo shift prima di sommare. Con $a+b=2^{16}$ il filtro $y=\big(a,y_{k-1}+b,(u\ll4)\big)\gg16$ non va in overflow in un uint32_t. Nel codice il formato di ogni variabile va dichiarato in un commento; la funzione $\sin x\approx x-\frac{x^3}6+\frac{x^5}{120}$ mostra come si approssima una funzione con polinomi a coefficienti in virgola fissa.Realizzare un filtro o un regolatore a virgola fissa →, Operazioni in virgola fissa - somma, prodotto e riallineamentoIn virgola fissa il processore opera sugli interi e non sa dov'è la virgola: tocca al programmatore. Regola 1: si sommano solo dati con lo stesso formato (stesso $m$). Regola 2: il prodotto di $n_1.m_1$ per $n_2.m_2$ ha $m_1+m_2$ bit frazionari e il doppio dei bit: va riallineato con uno shift a destra di $m_2$ posizioni (per riportarlo a $m_1$) o preso dalla parte alta. Con la normalizzazione frazionaria $1.15$ il prodotto è $2.30$ e basta uno shift a sinistra di 1 prima di prendere la parte alta. I fattori di scala si scelgono per evitare overflow e perdita di risoluzione.Operazioni in virgola fissa - somma, prodotto e riallineamento →, Rappresentare un valore reale in virgola fissa - formato ottimo ed errorePer rappresentare un reale $x$ su $W$ bit a virgola fissa si sceglie il formato che minimizza l'erroresceglie la rappresentazione per cui la differenza tra il valore rappresentato e quello vero è la più piccola possibile: il maggior numero di bit frazionari $m$ compatibile con l'intervallo ($n=W-m$ bit interi, con U se $x\ge0$, con S se $x<0$). Poi $C=\mathrm{round}(x\cdot2^m)$, scritto in esadecimale (per un negativo, in complemento a 2), e l'errore $\varepsilon=\frac{C}{2^m}-x$, positivo se in eccesso, ha modulo $\le2^{-(m+1)}$. Se invece è dato l'errore massimo, i bit minimi si ricavano da $2^{-(m+1)}\le\varepsilon_{max}$.Rappresentare un valore reale in virgola fissa - formato ottimo ed errore →.

(a) Coefficienti

  • a=0,8a=0{,}8: positivo e minore di 1: 0.16 U. a16=round(0,8⋅65536)=round(52428,8)=52429=0xCCCDa_{16}=\mathrm{round}(0{,}8\cdot65536)=\mathrm{round}(52428{,}8)=52429=\texttt{0xCCCD} (errore +3,1⋅10−6+3{,}1\cdot10^{-6}; con troncamento si avrebbe 0xCCCC\texttt{0xCCCC}).
  • b0=12,2b_0=12{,}2: positivo, <16<16: 4.12 U. b0,16=round(12,2⋅4096)=round(49971,2)=49971=0xC333b_{0,16}=\mathrm{round}(12{,}2\cdot4096)=\mathrm{round}(49971{,}2)=49971=\texttt{0xC333} (ε=−4,9⋅10−5\varepsilon=-4{,}9\cdot10^{-5}).
  • b1=−12b_1=-12: negativo, ∣b1∣<16|b_1|<16: serve il segno: 5.11 S (segno + 4 bit interi + 11 frazionari, intervallo [−16,16)[-16,16)). b1,16=−12⋅2048=−24576→65536−24576=40960=0xA000b_{1,16}=-12\cdot2048=-24576\to65536-24576=40960=\texttt{0xA000} (esatto).

(b) Espressione in C

Bit frazionari dei prodotti. yy è in 4.12 (m=12m=12); xx in 0.16 (m=16m=16).

  • a⋅yk−1a\cdot y_{k-1}: 16+12=2816+12=28 bit frazionari. Per portare il prodotto a 12 bit frazionari si fa ≫16\gg16.
  • b0⋅xb_0\cdot x: 12+16=2812+16=28; b1⋅xk−1b_1\cdot x_{k-1}: 11+16=2711+16=27. Per sommare i due prodotti li si porta alla stessa scala spostando b0b_0 a destra di 1 bit (b0≫1b_0\gg1 passa da 12 a 11 bit frazionari): (b0≫1)⋅x(b_0\gg1)\cdot x ha 11+16=2711+16=27 bit frazionari come b1⋅xk−1b_1\cdot x_{k-1}. La somma dei due ha 27 bit frazionari; per arrivare a 12 si fa ≫15\gg15.
c
// a16: 0.16 U (uint16_t); b0_16: 4.12 U (uint16_t); b1_16: 5.11 S (int16_t)
// x, x0: 0.16 U;  y, y0: 4.12 U (uint32_t)
y  = ((a16 * y0) >> 16) + ((((b0_16 >> 1) * x) + (b1_16 * x0)) >> 15);
y0 = y;  x0 = x;

Il calcolo è su 32 bit: a16*y0 con y0<65536y_0<65536 e a16<65536a_{16}<65536 sta in un uint32_t; per il termine con segno si usa int32_t (cast di b1_16 e x0 a int32_t prima del prodotto: b1⋅x0=−24576⋅65535=−1,6⋅109b_1\cdot x_0=-24576\cdot65535=-1{,}6\cdot10^9, ancora nei limiti di int32_t). Il primo prodotto non va in overflow (52429⋅65535=3,43⋅109<23252429\cdot65535=3{,}43\cdot10^9<2^{32}).

(c) Valore massimo di xx

Il prodotto b0xb_0x con x<1x<1 vale y(0)=12,2 xy(0)=12{,}2\,x al massimo: l'uscita yy è in 4.12 U, intervallo [0,16)[0,16): serve 12,2 x<16⇒x<1,3112{,}2\,x<16\Rightarrow x<1{,}31. Ma xx in 0.16 U è comunque minore di 1 (0xFFFF/65536=0,999980xFFFF/65536=0{,}99998): xmax=1x_{max}=1 (fondo scala), nessun overflow. (La risposta del testo: xmax=1x_{max}=1.)

Attenzione: per un gradino che scende da 1 a 0 l'uscita ha valore −11,2-11{,}2 al primo campione (0,8⋅1+0−12⋅10{,}8\cdot1+0-12\cdot1), negativo: con yy in formato senza segno si avrebbe un avvolgimento. Nell'uso reale va usata una variabile con segno (4.12 S o 5.11 S) se l'ingresso può diminuire; il testo ipotizza ingressi crescenti.

(d) Verifica con un gradino

Si applica x=0xFFFF (≈1)x=\texttt{0xFFFF}\ (\approx1) da k=0k=0 (y0=0y_0=0, x0=0x_0=0):

kk y(k)y(k) esadecimale valore valore teorico 1+11,2⋅0,8k1+11{,}2\cdot0{,}8^k
0 0xC331\texttt{0xC331} 12,1995 12,2
1 0x9F58\texttt{0x9F58} 9,959 9,96
2 0x82AA\texttt{0x82AA} 8,166 8,168
21 0x119A\texttt{0x119A} 1,1001 1,1033
22 0x1145\texttt{0x1145} 1,0793 1,0826

I valori coincidono con quelli teorici a meno di 3⋅10−33\cdot10^{-3} (errori di arrotondamento, soprattutto del termine x≈0,99998x\approx0{,}99998 e dei troncamenti degli shift). Il filtro entra nel 10%10\% del valore finale a k=22k=22 come previsto (y(21)=1,100y(21)=1{,}100 è quasi al limite).

Errori comuni

  • Rappresentare b1=−12b_1=-12 in formato senza segno: il codice 0xA000\texttt{0xA000} letto come 5.11 U vale +20+20, non −12-12.
  • Sommare (b0 x)(b_0\,x) con 28 bit frazionari e (b1 x0)(b_1\,x_0) con 27 senza riallineare.
  • Dimenticare il cast a 32 bit prima del prodotto (in C il prodotto di due uint16_t è un int con segno e può andare in overflow).
  • Applicare lo shift di 16 a tutti i termini senza controllare quanti bit frazionari ha ciascun prodotto.

Versione ripasso

Testo. Realizzare in C, a virgola fissa a 32 bit, y(k)=0,8y(k−1)+12,2x(k)−12x(k−1)y(k)=0{,}8y(k-1)+12{,}2x(k)-12x(k-1) con xx in 0.16 U e yy in 4.12 U (febbraio 2026).

Teoria collegata